树形背包[1/ 50] luogu [P2015] (超级板)
前言
这大概是一道树形背包的入门级题目了吧。笔者因对树形背包一窍不通,就只能从板题开始了
题目大意
有一棵二叉树,每一条树枝上都有一些价值,在符合保留的树枝条数的情况下要求价值最大。
思路
对于每一个子节点(叶子除外),下面都有两条树枝,而对于每条树枝,选择保留它,就等于选择了其到根节点上的路径的每一条树枝。(因此我们需要记录)。又根据贪心策略,我们最后的值要尽可能的大,也就等于在选择一条边后,下面的树枝所保留的苹果数要尽可能的大。那么这就符合了gm讲的DP的两点要求:最优子结构和重叠子问题因此我们选择使用DP。然而,这很明显是在一棵树上,也就非常自然的采用树形DP啦。
那我们的\(DP[i][j]\) 表示什么呢?及在第i个节点下留下j条边所保留的最大价值。
状态转移方程:
\(dp[i][j] = max(dp[i][j], dp[son][k] + apple[i][son] + dp[i] [j - k - 1]\)
代码
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
using namespace std;
const int maxn = 105;
struct node{
int apple, w;
};
int n, q;
vector<node> tree[maxn];
bool vis[maxn];
int dp[maxn][maxn]; //dp[i][j]表示第i个节点下留下j条边所保留的最大价值
void dfs(int x){
vis[x] = 1;
for(int i = 0; i < tree[x].size(); i ++){
node flag2 = tree[x][i];
if(!vis[flag2.w]){
dfs(flag2.w);
for(int j = q; j >= 0; j --){
for(int k = 0; k < j; k ++){
dp[x][j] = max(dp[x][j], dp[flag2.w][k] + dp[x][j - k - 1] + flag2.apple);
}
}
}
}
}
int main() {
scanf("%d %d", &n, &q);
for(int i = 1; i < n; i ++){
node flag;
int a, b, c;
scanf("%d %d %d", &a, &b, &c);
flag.apple = c;
flag.w = b;
tree[a].push_back(flag);
flag.w = a;
tree[b].push_back(flag); //领接表存边
}
dfs(1);
printf("%d", dp[1][q]);
return 0;
}
结语
可能是因为笔者太菜鸡了吧,在之前学习树形DP的时候,就没有怎么打出来(还立下了个flag贴),前几天复习的时候,基本上跟树形背包有关的问题都打不出来,大佬们讲了思路也无法实现。所以也就只有从这种很基础的板题开始了。
跪求大佬轻喷。。。orz
(please不要在这篇blog,和笔者的那篇flag帖下面说笔者刷水题,谢谢)
夜空中最亮的星,请照亮我前行