Codeforces Round #551 (Div. 2) :D Serval and Rooted Tree(贪心 + 树形dp)

题目大意:给出一棵树,1号结点为根节点,设该树有k个叶子结点,叶子结点的值可以任意赋值,但必须用[1,k]范围内的数,且每个数只能用一次,除此之外每个结点还有一个权值,权值1代表该结点的值为儿子结点中权值最大的一个值,权值0表示该点的值为儿子结点中最小的值。现在问根节点的最大值是多少?

解法:问题可以转变为,至少要设置多少个叶子结点的值才能使得根节点有值?(看到这句话是否恍然大悟?)若放x个叶子结点才能使得根节点有值,那么根节点的值为:k - x + 1。

这明显可以树形dp,设dp[i]为传递到结点i至少要设多少个叶子结点的值,若当前结点v的权值为1,则dp[v] 等于儿子节点中dp值最小的一个。若当前节点v的权值为0,则dp[v]为儿子结点dp值的和。

以下文字来源于大佬博客,借鉴博客地址:https://www.cnblogs.com/00isok/p/10705602.html
不难想到,本题就是转化成求在满足题目求的情况下,传递到根节点的最少叶子节点个数。因为叶子节点的值是由我们自己够构造,所以我们只需要将那些能够传递到根节点的叶子节点,从大到小赋值,就能够让根节点的值最大,且它的最大值为ans-num+1(ans为叶子节点个数,num为传递到根节点的叶子节点个数)。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 3e5 + 10;
vector<int> g[maxn];
int n;
int f[maxn],fa[maxn],dp[maxn];
int dfs(int s) {
	int res = 0;
	if(f[s] == 1)
		dp[s] = 0x3f3f3f3f;
	else dp[s] = 0;
	if(!g[s].size()) {
		dp[s] = 1;
		return 1;
	}
	for(int i = 0; i < g[s].size(); i++) {
		int v = g[s][i];
		res += dfs(v);
		if(f[s] == 1)
			dp[s] = min(dp[s],dp[v]);
 		else {
 			dp[s] += dp[v];	
		}
	}
	return res;
}
int main() {
	scanf("%d",&n);
	for(int i = 1; i <= n; i++) {
		scanf("%d",&f[i]);
	}
	for(int i = 2; i <= n; i++) {
		int fa;
		scanf("%d",&fa);
		g[fa].push_back(i);
	}
	int tot = dfs(1);
	printf("%d\n",tot - dp[1] + 1);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_41997978/article/details/89764714