題目:
時間限制: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, 找一個只有三個節點的情況仔細分析一下最容易獲得狀態轉移方程:
-
葉子節點
-
非葉子頂點 , 其從做到右第 個孩子結點爲 , 其權重爲
-
最終結果爲(爲選擇的樹的根節點)
關鍵點:
- 怎麼存儲這個樹?
因爲給出兩個點但是沒有說誰是父親誰是孩子, 因此使用鄰接表存儲一個無向圖的方式來表示。
- 超時問題:
我第一次使用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;
}