树形背包[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帖下面说笔者刷水题,谢谢)

posted @ 2020-11-27 21:40  cqbzzyq  阅读(43)  评论(0编辑  收藏  举报