算法訓練 節點選擇

問題描述

有一棵 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的題目, 首先給出狀態轉移方程:
dp[i][2], 1代表選擇i, 0代表不選擇i
i代表父節點, j代表子節點, 當選擇i節點時, j節點必然不選擇, 當不選擇i節點時, 則比較選擇j節點和不選擇j節點的情況選擇大的

dp[i][1] = dp[j][0]
dp[i][0] = max(dp[j][0], dp[j][1])  

一開始題沒看仔細, 以為輸入在前的代表父節點, 輸入在后的代表子節點, 所以就錯了:

#include <bits/stdc++.h>
#define MAXN 100010
using namespace std;

int dp[MAXN][2], father[MAXN];
bool visited[MAXN];
int n, root;

void treeDp(int node) {
    visited[node] = true;
    for(int i = 1; i <= n; i++) {
        if(!visited[i] && father[i] == node) {
            treeDp(i);
            dp[node][1] += dp[i][0];
            dp[node][0] += max(dp[i][0], dp[i][1]);
        }
    }
}

int main() {
    scanf("%d", &n);
    for(int i = 1; i <= n; i++) {
        scanf("%d", &dp[i][1]);
    }
    int f, c;
    bool begin = true;
    root = 0;
    for(int i = 1; i < n; i++) {
        scanf("%d%d", &f, &c);
        father[c] = f;
        if(root == c || begin) {
            root = f;
            begin = true;
        }
    }
    while(father[root]) {
        root = father[root];
    }
     
    treeDp(root);
    int ans = max(dp[root][0], dp[root][1]);
    printf("%d\n", ans);
    
    return 0;
} 

還是too young too simple啊, 這里只是告訴了兩個節點形成了一條邊, 并沒有告訴誰是父節點, 誰是子節點, 所以這里需要使用前向星來存儲樹

#include <bits/stdc++.h>
#define MAXN 100010
using namespace std;

struct {
    int v;
    int next;
}edge[MAXN * 2];
int dp[MAXN][2], head[MAXN];
int M;

void add(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++;
}

void treeDp(int x, int pre) {
    for (int i = head[x]; i != -1; i = edge[i].next) {
        int v = edge[i].v;
        if(pre == v)
            continue;
        treeDp(v, x);
        dp[x][1] += dp[v][0];
        dp[x][0] += max(dp[v][0], dp[v][1]); 
    }
}

int main() {
    int n;
    M = 0;
    scanf("%d", &n);
    memset(dp, 0, sizeof(dp));
    memset(head, -1, sizeof(head));
    for (int i = 1; i <= n; i++) {
        scanf("%d", &dp[i][1]);
    }
    int c, f;
    for(int i = 1; i < n; i++) {
        scanf("%d%d", &c, &f);
        add(c, f);
    }
    treeDp(1, -1);
    int ans = max(dp[1][0], dp[1][1]);
    printf("%d\n", ans);
    
    
    return 0;
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 樹形動態規劃,顧名思義就是樹+DP,先分別回顧一下基本內容吧:動態規劃:問題可以分解成若干相互聯系的階段,在每一個...
    Mr_chong閱讀 1,522評論 0 2
  • 1.把二元查找樹轉變成排序的雙向鏈表 題目: 輸入一棵二元查找樹,將該二元查找樹轉換成一個排序的雙向鏈表。 要求不...
    曲終人散Li閱讀 3,371評論 0 19
  • 我以為我不會難過 怎么能這么快又墜入情網呢 像一個接一個的深淵 一不小心失足就跌入萬劫不復境地 沒有酒喝 大姨媽痛...
    塵彌閱讀 305評論 0 0
  • 雜草袁閱讀 125評論 0 1
  • 寒風,晚來雪,夜歸人。 喜歡在這寂冷的冬日,伴著輕柔的音樂,來到文字的長河里倘佯。 一直用清冷的目光注視著周邊的世...
    木心幽蘭閱讀 349評論 0 1