算法訓練 結點選擇

題目:

時間限制:1.0s   內存限制:256.0MB
   
問題描述

有一棵 n 個節點的樹,樹上每個節點都有一個正整數權值。如果一個點被選擇了,那麼在樹上和它相鄰的點都不能被選擇。求選出的點的權值和最大是多少?
輸入格式

第一行包含一個整數 n 。

接下來的一行包含 n 個正整數,第 i 個正整數代表點 i 的權值。

接下來一共 n-1 行,每行描述樹上的一條邊。
輸出格式
輸出一個整數,代表選出的點的權值和的最大值。
樣例輸入
5
1 2 3 4 5
1 2
1 3
2 4
2 5
樣例輸出
12
樣例說明
選擇3、4、5號點,權值和爲 3+4+5 = 12 。
數據規模與約定

對於20%的數據, n <= 20。

對於50%的數據, n <= 1000。

對於100%的數據, n <= 100000。

權值均爲不超過1000的正整數。


分析:

        歸類爲樹形dp, 找一個只有三個節點的情況仔細分析一下最容易獲得狀態轉移方程:

  • 葉子節點 vv
    dp[v][0]=0;dp[v][0] = 0;dp[v][1]=weight[i];dp[v][1] = weight[i];

  • 非葉子頂點 vv, 其從做到右第 ii 個孩子結點爲 sis_{i}, 其權重爲weightvweight_{v}
    dp[v][0]=i=1nmax(dp[si][1],dp[si][0])dp[v][0] = \sum_{i=1}^{n}max(dp[s_{i}][1], dp[s_{i}][0])dp[v][1]=i=1ndp[si][0]+weightvdp[v][1] = \sum_{i = 1}^{n}dp[s_{i}][0] + weight_{v}

  • 最終結果爲(rootroot爲選擇的樹的根節點)
    max(dp[root][0],dp[root][1])max(dp[root][0], dp[root][1])


關鍵點:

  • 怎麼存儲這個樹?

         因爲給出兩個點但是沒有說誰是父親誰是孩子, 因此使用鄰接表存儲一個無向圖的方式來表示。

  • 超時問題:

         我第一次使用C++提供的STL來常見鄰接表, 直接50%的超時。

map<int, vector<int> >;

        後修改爲如下結構的方式超時問題解決:

int m = 0;
// 表示一個樹結點的鏈表結點, 使用靜態數組的方式加快速度
struct edge
{
    int v, int next;
}edges[200001];
head[100001];      // memset(head, -1, sizeof(head))

void addEdge(int from, int to){
    edge[m].v = to;
    edge[m].next = head[from];
    head[from] = m++;
    
    edge[m].v = from;
    edge[m].next = head[to];
    head[to] = m++;
    
};

  • 如何解決無向圖表示的樹的後序遍歷的無限遞歸問題

         在DFS函數當中添加一個pre參數, 表明其父親節點即可, 當孩子不等於父親則可以DFS。


代碼:

#include <bits/stdc++.h>
using namespace std;
int N, t, b, e;
int weights[100001];
int head[100001];           /* 鄰接表頭*/           
int dp[100001][2];     
int m = 0;                  /* 指針*/
struct edge{                /* 鄰接表使用的邊節點*/
    int v, next;
}edges[200001];


void addEdge(int from, int to)
{
    // 以to爲終點的邊, 頭查的方式插入
    edges[m].v = to;
    edges[m].next = head[from];
    head[from] = m++;

    // 以from爲終點的邊
    edges[m].v = from;
    edges[m].next = head[to];
    head[to] = m++;
}

void DFS(int root, int pre)
{
    dp[root][0] = 0;
    dp[root][1] = weights[root];

    // 遍歷鏈表
    // cout << root << endl;
    for(int n = head[root]; n != -1; n = edges[n].next){
        int v = edges[n].v;
    // if(root == 1) cout << v <<endl;
        if(v == pre) continue;
        DFS(v, root);     // 後序遍歷
        dp[root][0] += max(dp[v][0], dp[v][1]);
        dp[root][1] += dp[v][0];
    }
    //printf("dp[%d][0]  = %d -- dp[%d][1] = %d\n", root, dp[root][0], root ,dp[root][1]);
}

int main()
{
#ifdef LOCAL
    freopen("ALGO-4.in", "r", stdin);
#endif
    std::ios::sync_with_stdio(false);
    memset(head, -1, sizeof(head));
    int ans = 0;
    cin >> N;
    for(int i = 1; i <= N; ++i)
        cin >> t, weights[i] = t;
    while(cin >> b >> e)
        addEdge(b, e);
    DFS(1, -1); 
    cout << max(dp[1][0], dp[1][1]) << endl;
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章