**
題目:
問題描述
有一棵 n 個節點的樹,樹上每個節點都有乙個正整數權值。如果乙個點被選擇了,那麼在樹上和它相鄰的點都不能被選擇。求選出的點的權值和最大是多少?
輸入格式
第一行包含乙個整數 n 。
接下來的一行包含 n 個正整數,第 i 個正整數代表點 i 的權值。
接下來一共 n-1 行,每行描述樹上的一條邊。
輸出格式
輸出乙個整數,代表選出的點的權值和的最大值。
樣例輸入
51 2 3 4 5
1 21 3
2 42 5
樣例輸出
12樣例說明
選擇3、4、5號點,權值和為 3+4+5 = 12 。
資料規模與約定
對於20%的資料, n <= 20。
對於50%的資料, n <= 1000。
對於100%的資料, n <= 100000。
權值均為不超過1000的正整數。
看過這道題,我們大概可以知道,這是一道和樹有關的問題,那麼我們需要考慮的就是如何去遍歷樹?很明顯,我們需要用到dfs。仔細讀這道題,然後我們就可以得到這還需要用到動態規劃,由此,我們做這道題,解題方案也就是樹形動態規劃了!
既然要用動態規劃,構造狀態轉移方程是必不可少了!
對於葉子結點:
dp[k][0] = 0;
dp[k][1] = k點權值;
對於非葉子結點:
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])。(要麼不包括根結點,要麼包括根結點)
**如下:
#include
#include
#define _max 100010
#define max(a, b) a > b ? a : b
struct point
edge[_max * 2]; //一條邊記錄兩次,分別以乙個點做記錄
int head[_max];
int m;
int dp[_max][2];
//新增乙個邊
void addedge(int from, int to)
//深度遍歷,先深入到葉子結點,然後一層一層往上回公升,一直到根結點,即第乙個結點(初始pre為-1是因為根結點沒有父結點,用-1表示)
void dfs(int x, int pre)
dfs(v, x); //x可以理解為父結點
//深度遍歷到最裡面的葉子結點的父結點 如果父結點選擇,則子結點不選擇,否則子結點可能選擇或者不選擇,但是要比較兩者哪個**擇哪個
dp[x][1] += dp[v][0]; // 父結點(選) += 子結點(不選)
dp[x][0] += max(dp[v][0], dp[v][1]); // 父結點(不選) += max(子結點(不選),子結點(選))
}return ;
}int main(int argc, const
char * argv)
//輸入邊,並且新增edge,乙個邊新增兩個edge
for (i = 1; i < n; i++)
dfs(1, -1); //深度優先遍歷,從第乙個結點開始遍歷
tmp = max(dp[1][0], dp[1][1]); //求出最大的權值和
printf("%d\n", tmp);
return
0;}
具體的思路實現都注釋了,希望可以幫助理解。
over!!!
藍橋杯 演算法訓練 結點選擇
問題描述 有一棵 n 個節點的樹,樹上每個節點都有乙個正整數權值。如果乙個點被選擇了,那麼在樹上和它相鄰的點都不能被選擇。求選出的點的權值和最大是多少?輸入格式 第一行包含乙個整數 n 接下來的一行包含 n 個正整數,第 i 個正整數代表點 i 的權值。接下來一共 n 1 行,每行描述樹上的一條邊。...
藍橋杯 演算法訓練 結點選擇
題目問題描述 有一棵 n 個節點的樹,樹上每個節點都有乙個正整數權值。如果乙個點被選擇了,那麼在樹上和它相鄰的點都不能被選擇。求選出的點的權值和最大是多少?輸入格式 第一行包含乙個整數 n 接下來的一行包含 n 個正整數,第 i 個正整數代表點 i 的權值。接下來一共 n 1 行,每行描述樹上的一條...
藍橋杯 結點選擇
問題描述 有一棵 n 個節點的樹,樹上每個節點都有乙個正整數權值。如果乙個點被選擇了,那麼在樹上和它相鄰的點都不能被選擇。求選出的點的權值和最大是多少?解題思路 這題模型是樹形動態規劃入門題目,dp i 0 表示該節點不被選擇,dp i 1 表示該結點被選擇。轉移方程為 dp u 1 dp v 0 ...