目錄
1 問題描述
有一棵 n 個節點的樹,樹上每個節點都有一個正整數權值。如果一個點被選擇了,那么在樹上和它相鄰的點都不能被選擇。求選出的點的權值和最大是多少?
第一行包含一個整數 n 。
接下來的一行包含 n 個正整數,第 i 個正整數代表點 i 的權值。
接下來一共 n-1 行,每行描述樹上的一條邊。
1 2 3 4 5
1 2
1 3
2 4
2 5
對於20%的數據, n <= 20。
對於50%的數據, n <= 1000。
對於100%的數據, n <= 100000。
權值均為不超過1000的正整數。
2 解決方案
本題主要考查動態規划法,核心在於如何構造狀態轉移方程。
引用參考資料2中講解:
DP, 用dp[i][0]表示不選擇i點時,i點及其子樹能選出的最大權值,dp[i][1]表示選擇i點時,i點及其子樹的最大權值。
狀態轉移方程:
對於葉子節點 dp[k][0] = 0, dp[k][1] = k點權值
對於非葉子節點i,
dp[i][0] = ∑max(dp[j][0], dp[j][1]) (j是i的兒子)
dp[i][1] = i點權值 + ∑dp[j][0] (j是i的兒子)
最大權值即為max(dp[0][0], dp[0][1])
下面的代碼最終運行評分為50分,具體原因:運行超時。不過文末參考資料1中C++代碼運行評分為100分(PS:具體理解可以參考文末參考資料1)。看了參考資料1中代碼,讓我對題意的理解變為:其中輸入n-1條表示邊的兩個數,是具體的第i個頂點和第j個頂點,不是具體頂點的權值;在第一次做的時候,我理解為具體頂點的權值。
具體代碼如下:
import java.util.Scanner; public class Main { public int[][] dp = new int[100002][2]; public int[][] tree = new int[100002][300]; //tree[i][3] = num表示第i個節點的第3個孩子節點為第num個節點 /* * 參數point1:表示輸入的第point1個節點,不是節點權值 * 參數point2:表示輸入的第point2的節點,不是節點權值 * 說明:由於題目僅僅給出邊的說明,並未說明兩個節點誰是父母節點,所以以下有兩種情形 */ public void creatTree(int point1, int point2) { int i = 0; //當第point1個節點為父母節點時 while(tree[point1][i] != 0) i++; //如果第point1個節點已經有孩子了,再增加一個孩子 tree[point1][i] = point2; int j = 0; //當第point2個節點為父母節點時 while(tree[point2][j] != 0) j++; tree[point2][j] = point1; } /* * 參數satrt:開始對樹進行DFS遍歷的開始節點,為具體節點位置,不是節點權值 * 參數root:為第start個節點的直接父母節點位置,root = 0表示根節點的父母節點 */ public void dfs(int start, int root) { int child = tree[start][0]; //第start個節點的第1個孩子節點 for(int i = 0;child != 0;i++) { child = tree[start][i]; if(child != root) { //防止出現start的孩子成為start的父親情況 dfs(child, start); dp[start][1] += dp[child][0]; //當第child個節點沒有孩子節點時,開始回溯 dp[start][0] += (dp[child][1] > dp[child][0] ? dp[child][1] : dp[child][0]); } } } public static void main(String[] args) { Main test = new Main(); Scanner in = new Scanner(System.in); int n = in.nextInt(); for(int i = 0;i < n;i++) test.dp[i + 1][1] = in.nextInt(); for(int i = 0;i < n - 1;i++) { int point1 = in.nextInt(); int point2 = in.nextInt(); test.creatTree(point1, point2); } test.dfs(1, 0); //從創建的數的根節點(即第1個頂點,0表示根節點的父母節點)開始進行DFS遍歷 int max = (test.dp[1][1] > test.dp[1][0] ? test.dp[1][1] : test.dp[1][0]); System.out.println(max); } }
參考資料:
2. 藍橋杯 - 算法訓練 - ALGO - 4 結點選擇 (經典樹形DP)