1. 程式人生 > >一道簡單樹形dp

一道簡單樹形dp

必須 初始化 == oid color 關心 就是 oot 初始

題意:給定一棵樹,從中選出一些節點,使得不成父子關系的節點對數最多。問這個最大值是多少。

思路:首先既然是給定一顆樹,先要選擇合適的數據結構,來保存這顆樹。由於這顆樹只關心根節點在哪裏,所以只需要用一個fa數組來保存每個點的根節點,此外設初始化為0,所以根節點的fa[root]為0,所以從任意一個結點的父節點往上遍歷,直到遍歷到某某個結點的父節點為0,則這個結點就是父節點

這道題個人感覺關鍵是從根節點開始往下dp,所以根節點要確定..

這道題的狀態轉移方程為,dp[i][1],選用第i個節點時,所能得到的最大對數,dp[i][0]不選用第i個節點時所能得到的最大對數,dp[i][0]=求和max(dp[j][0],dp[j][1]);dp[i][1]=求和dp[j][0].所以每一個父節點都依賴於它的子節點。所以遞歸求每一個子節點的dp[j]的值,最後在累加到dp[i]上,而邊界條件是,遞歸到葉子節點時,dp[i][0]=0,dp[i][1]=1;加上代碼

int n;  // 結點個數
int dp[maxn][2];  // dp[i][0] 表示不選擇結點 i,dp[i][1] 表示選擇結點 i
int father[maxn];  // father 記錄了結點的父結點編號

void tree_dp(int node) {
    dp[node][0] = 0;
    dp[node][1] = 1for(int i = 1; i <= n; i++) {
        if(father[i] == node) {  // i 為 node 的子結點
            tree_dp(i);  // 遞歸計算子結點
            
// 關鍵 dp[node][1] += dp[i][0]; // 選擇父結點,則必須不選擇子結點 dp[node][0] += max(dp[i][1], dp[i][0]); // 不選擇父結點,則可以選擇或不選擇子結點 } } } int main() { int f, c, root; scanf("%d", &n); memset(father, 0, sizeof(father)); memset(visited, 0, sizeof(visited)); root = 0; // 記錄樹的根結點
while (scanf("%d %d", &c, &f), c || f) { // 讀入父子關系,前一個結點是後一個結點的孩子 father[c] = f; root = f; } while(father[root]) { // 查找根結點 root = father[root]; } tree_dp(root); // 從根結點出發進行動態規劃 printf("%d\n", max(dp[root][0], dp[root][1])); // 求出最終答案,根可以選或不選 return 0; }

一道簡單樹形dp