算法筆記_076:藍橋杯練習 結點選擇(Java)


目錄

1 問題描述

2 解決方案

 


1 問題描述

問題描述

有一棵 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的正整數。

 

 

 


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分,具體原因:運行超時。不過文末參考資料1C++代碼運行評分為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);
    }
}

 

 

 

參考資料:

1. 藍橋杯 算法提高 結點選擇(樹形DP)

2. 藍橋杯 - 算法訓練 - ALGO - 4 結點選擇 (經典樹形DP)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM