1. 程式人生 > 其它 >動態規劃:P2015二叉蘋果樹 樹形DP 分組揹包

動態規劃:P2015二叉蘋果樹 樹形DP 分組揹包

P2015二叉蘋果樹 題目傳送門:P2015 二叉蘋果樹 - 洛谷 | 電腦科學教育新生態 (luogu.com.cn) 題目:

 

 思路分析:

  觀察題目可以看出,這是一個有邊權的二叉樹。找到一顆子樹,使邊的個數滿足題目限定的數量,並且邊權和最大。顯然是樹形DP 要用分組揹包的思路,構造二維DP陣列,dp[i][j]代表的時候以i為根節點,連線j個枝條的時候,的總權值和。但是注意分組揹包的時候,對於每一結點,利用分組揹包DP模板時,要考慮取值範圍,j的範圍是min(m,size[本結點]),size陣列的意思就是從上往下搜,這個結點向下連的總枝條數,這裡有個技巧,不用再一個函式來計算連線的枝條數,直接在dfs裡計算,在for迴圈遍歷鄰接點時,dfs鄰接點後,size(根節點)+=size(鄰接點)+1,+1就是+根節點和鄰接點的枝條,因為dfs鄰接點在計算size之前,所以size(鄰接點)一定是算出來的,考慮葉子結點,葉子結點不進行for迴圈,枝條數為0,因為葉子結點向下連的枝條數為0,顯然這樣做是可以的。

  關鍵DP程式碼:

 

 

 總程式碼:

 1 #include<iostream>
 2 #include<algorithm>
 3 #include<vector>
 4 using namespace std;
 5 const int maxn = 3 * 1e2 + 5;
 6 const int inf = 0x7fffffff;
 7 int n, m;
 8 int dp[maxn][maxn];//表示以i為起點 留j個枝條 的權值和
 9 int head[maxn], val[2 * maxn], to[2 * maxn], nex[2
* maxn], index; 10 int siz[maxn]; 11 void add(int a, int b, int c) 12 { 13 index++; 14 to[index] = b; 15 nex[index] = head[a]; 16 head[a] = index; 17 val[index] = c; 18 } 19 void dfs(int u, int fa) 20 { 21 for (int i = head[u]; i; i = nex[i]) 22 { 23 int x = to[i];
24 if (x == fa)continue;//避免向上查詢 25 dfs(x, u); 26 siz[u] += (siz[x] + 1);//最大能連線的枝條數 關鍵 27 //開始01揹包 28 for (int j = min(m, siz[u]); j >= 1; --j)//j最大隻能到m 所以要用min函式 j最小也得取1 29 { 30 for (int k =0; k <= min(j - 1, siz[x]); ++k)//k最多隻能到j-1 因為從根開始 第一個必須要 也就是u結點必須選 31 { 32 dp[u][j] = max(dp[u][j], dp[u][j - k - 1] + dp[x][k] + val[i]);//注意j-k-1,因為連線u 和x還有一個樹枝 33 // cout << u << " " << j << ":" << dp[u][j] << endl << endl; 34 } 35 } 36 37 } 38 } 39 int main() 40 { 41 cin >> n >> m; 42 for (int i = 1; i < n; ++i) 43 { 44 int a, b, c; 45 cin >> a >> b >> c; 46 add(a, b, c); 47 add(b, a, c); 48 } 49 dfs(1, -1); 50 cout << dp[1][m]; 51 return 0; 52 }

 

通過: