動態規劃:P2015二叉蘋果樹 樹形DP 分組揹包
阿新 • • 發佈:2022-05-07
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 }
通過: