本人是一個剛剛接觸C++不久的傻學生~記錄一些自己的學習過程。大神路過可以批評指正~
剛學動態規划,水平還很渣,一下子不知道從何下手,借鑒了一下這位大哥的文章
http://www.cnblogs.com/yifan2016/p/5268887.html
問題描述
有一棵 n 個節點的樹,樹上每個節點都有一個正整數權值。如果一個點被選擇了,那么在樹上和它相鄰的點都不能被選擇。求選出的點的權值和最大是多少?
輸入格式
第一行包含一個整數 n 。
接下來的一行包含 n 個正整數,第 i 個正整數代表點 i 的權值。
接下來一共 n-1 行,每行描述樹上的一條邊。
輸出格式
輸出一個整數,代表選出的點的權值和的最大值。
樣例輸入
5
1 2 3 4 5
1 2
1 3
2 4
2 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[x][0]表示x結點不選中時最大的權值,dp[x][1]表示x結點選中時最大的權值
狀態轉移方程:dp[x][1] = dp[x][1] + dp[u][0] (u為x的子結點)
dp[x][0] = dp[x][0] + max{dp[u][0],dp[u][1]}(u為x的子結點)
代碼如下:
#include<cstdio> #include<cstring> #include<iostream> using namespace std; #define max(a,b) a>b?a:b const int MAXN = 100001; int M; //表示邊的索引號,初始為0 int head[MAXN]; //表示某個結點所連接的邊 int dp[MAXN][2]; //dp[x][0]表示第x個結點不選擇時最大權值,dp[x][1]表示第x個結點選擇時最大權值 struct Edge{ int toNode; //表示這條邊到達的結點 int nextEdge; //表示這條邊的出發結點連接的下一條邊 }edge[2*MAXN]; //一共有n個結點,有n-1條邊,但是不同的出發結點算作不同的邊,所以有2n-2條邊 //把新邊加入邊集,構造樹 void add(int from, int to){ edge[M].toNode = to; edge[M].nextEdge = head[from]; head[from] = M++; //head[x]的值可能會被二次賦值 } //類似dfs遍歷 void dfs(int node, int preNode){ for (int i = head[node]; i != -1; i = edge[i].nextEdge){ if (edge[i].toNode == preNode) //說明這條邊已經搜索過 continue; int toNode = edge[i].toNode; //表示邊i到達的結點 dfs(toNode, node); dp[node][0] += max(dp[toNode][0], dp[toNode][1]); //該結點不算,則該邊上的另一結點可選也可不選 dp[node][1] += dp[toNode][0]; //改結點選了,該邊上另一結點就不能選了 } } int main(){ int n; memset(head, -1, sizeof(head)); //所有邊置為-1,表示不存在該邊 memset(dp, 0, sizeof(dp)); cin >> n; for (int i = 1; i <= n; i++){ cin >> dp[i][1]; //每一個結點的權值 } for (int j = 1; j <= n - 1; j++){ int from, to; cin >> from >> to; add(from, to); add(to, from); } dfs(1, 0); //從1號結點開始向后動態規划 int result = max(dp[1][0], dp[1][1]); //因為不確定根結點,所以從幾號開始動態規划就找幾號的狀態 //同樣這里也可以寫成 dfs(2, 0); int result = max(dp[2][0], dp[2][1]);不過當只有一個結點的時候就不對了 cout << result << endl; return 0; }