本题中很大不同点在于他给的是边的数值,而正常的树的问题是在节点上给值。那么我们采用边值下放点的做法,这样就是正常的树问题了。
那么对于某一个节点来说它保留j个节点所得到的最大苹果树取决于这j个节点来自于左边子树多少,来自右边子树多少。又因为他自身就有一个节点所以状态转移方程为:
dp[i][need] = max(dp[i][need], num_l+num_r+tree[i].val);
但是要注意对于第一个节点传递的时候需要传递q+1,因为别的节点都有父亲,所以他们本身也贡献一个树枝。但根节点没有,为了保证一致性所以传递q+1。
还有这题使用记忆化搜索最简单,因为使用单纯的状态转移的话对于转移的范围不靠控制,还得知道子树下面到底有多少个节点。
#include <bits/stdc++.h> using namespace std; const int maxnf = 100+10; const int maxn = 100*2+10; struct tree{ int l, r; int val; } tree[maxnf]; int dp[maxnf][maxnf]; //使用链式前向星去表示一棵树。 int lt[maxn], nt[maxn], ed[maxn], nm[maxn]; int cnt = 0; int n, q; void addd(int x, int y, int val) { ed[++cnt] = y; nt[cnt] = lt[x]; lt[x] = cnt; nm[cnt] = val; } void init(int x, int fa) { for (int i=lt[x];i;i = nt[i]) { int y = ed[i]; if (y==fa) continue; if (!tree[x].l) tree[x].l = y; else tree[x].r = y; tree[y].val = nm[i]; init(y, x); } } //状态转移方程:dp[i][j] = max(dp[l][k], dp[r][j-k-1])+tree[i].val; int dfs(int i, int need) { int l = tree[i].l;int r = tree[i].r; if (i==0) return 0; if (dp[i][need]!=0) return dp[i][need]; for (int k=0;k<need;k++) { int num_l = dfs(tree[i].l, k); int num_r = dfs(tree[i].r, need-k-1); dp[i][need] = max(dp[i][need], num_l+num_r+tree[i].val); } return dp[i][need]; } int main() { int x, y, val; cin>>n>>q; for (int i=1;i<n;i++) { cin>>x>>y>>val; addd(x, y, val); } //将边值变成点值,方便后面的处理。 init(1, 0); cout<<dfs(1, q+1); return 0; }