1. 程式人生 > >[Luogu2015] 二叉蘋果樹 [樹形dp]

[Luogu2015] 二叉蘋果樹 [樹形dp]

[ L i n k \frak{Link} ]


感覺自己在理清碼程式碼思路上面還是有很大的不足
用樹形dp練習一下。


我就卜把這題當作二叉樹,直接套揹包啦
f(x, c)

,表示以x為根的子樹保留c條枝最多能夠留下多少蘋果。
那麼,分類討論:
對於某個點把它保留的c條枝頭分別分給它的每個子節點
也就是說讓每個子節點用一定的價值給父親一定的貢獻。
價值怎麼分配?? 這個問題有一點點模糊,不過它實際上是一個很熟悉的模型
相當於某個子節點可以任取代價,某個代價可以得到某種價值
這是多重揹包的經典模型。


進一步考慮,化為直觀的狀態和方程。
題目要求是選一個點子樹中的點那麼這個點必須沒有被剪
於是f(x,c) = max{f(x,k)+Σf(childi,xi)+val(edge)}, Σxi=c-k-1
至於不保留這個點的情況就是f(x,0),會被父親考慮到
因為根節點一定會被選(剪不掉),所以這樣設計狀態正好合適。


#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<cctype>
#include<ctime>
using namespace std;
#define add_edge(a, b, c) nxt[++tot] = head[a], head[a] = tot, to[tot] = b, val[tot] = c
int n, q, tot; int head[105], nxt[205], to[205], val[205], siz[105]; int f[105][105]; void dfs(int x) { siz[x] = 1; for (int i = head[x]; i; i = nxt[i]) { dfs(to[i]); siz[x] += siz[to[i]]; } for (int i = head[x]; i; i = nxt[i]) { for (int k = siz[x] - 1; k >= 0; --k) { for (int j = 0; j < siz[to[i]]; ++j) { if (j >= k) continue; f[x][k] = max(f[x][k], f[x][k-j-1] + f[to[i]][j] + val[i]); } } } } int main() { scanf("%d%d", &n, &q); for (int a, b, c, i = 1; i < n; ++i) { scanf("%d%d%d", &a, &b, &c); add_edge(a, b, c); } dfs(1); printf("%d", f[1][q]); return 0; }