P4107题解

P4107题解

题目大意:

1、这是一个有根树。
2、对于每一个节点,它的子节点数量和自身权值和不能超过
指定的m。
3、删除一个节点就是将其子节点连到其父节点上,点权利也
加到父节点上。
4、要使得删除次数最多

莫名想起消消乐

解题思路

首先,一眼望过去,不是 \(\text{dp}\) 就是贪心

很明显,子问题都没有独立性,所以直接排除 \(\text{dp}\)

所以,一定是贪心。

使用数组 weight 来表示某一个节点的重量(\(weight[i] = son(i) + c[i]\))

对于每一个节点,将它所有的子节点的 weight 依次存储于数组 a 中

然后把 \(a~\text{sort}\) 一遍

(这时候\(a\)的作用就很明了了,为了使原本的weight不变)

从小到大排序后,从第一个开始依次遍历

只要使得 \(weight[u] + a[v] - 1 <= m\) 就把他删掉(u是当前节点,

v是它的子节点)

那么就删除节点v

而对于每一个节点,要将其子节点的 weight 值全都处理完毕才能

处理此节点

所以我们要使用 \(\text{dfs}\) 来实现

核心代码

这里提供两个版本

1、 \(\text{vector}\) 版本

void dfs(int u)
{
	for(int i = 0;i < G[u].size();i++) dfs(G[u][i]);
	int tot = 0;
	for(int i = 0;i < G[u].size();i++) a[++tot] = weight[G[u][i]];
	sort(a + 1,a + tot +1);
	for(int i = 1;i <= tot;i++)
	{
		if(weight[u] + a[i] - 1 <= m)
		{
			weight[u] += a[i] - 1;
			ans++;
		}
		else break;
	}
}

2、邻接表版本

void dfs(int u)
{
	for(int i = head[u];i;i = e[i].next) dfs(e[i].to);
	int tot = 0;
	for(int i = head[u];i;i = e[i].next) a[++tot] = weight[e[i].to];
	sort(a + 1,a + tot +1);
	for(int i = 1;i <= tot;i++)
	{
		if(weight[u] + a[i] - 1 <= m)
		{
			weight[u] += a[i] - 1;
			ans++;
		}
		else break;
	}
}

代码

注意一下,这道题不能使用 \(\text{cin}\)\(\text{cout}\) ,会超时

#include <bits/stdc++.h>
using namespace std;

int n,m,ans;
int val[2000010],a[2000010];
int weight[2000010];
vector <int> G[2000010];

void dfs(int u)
{
	for(int i = 0;i < G[u].size();i++) dfs(G[u][i]);
	int tot = 0;
	for(int i = 0;i < G[u].size();i++) a[++tot] = weight[G[u][i]];
	sort(a + 1,a + tot +1);
	for(int i = 1;i <= tot;i++)
	{
		if(weight[u] + a[i] - 1 <= m)
		{
			weight[u] += a[i] - 1;
			ans++;
		}
		else break;
	}
}

int main()
{
	cin >> n >> m;
	for(int i = 1;i <= n;i++) scanf("%d",&val[i]);
	for(int i = 1;i <= n;i++)
	{
		int x,y;
		scanf("%d",&x);
		weight[i] = x + val[i];
		for(int j = 1;j <= x;j++)
		{
			scanf("%d",&y);
			G[i].push_back(y + 1);
		}
	}
	dfs(1);
	printf("%d",ans); 
    return 0;
}
posted @ 2021-08-05 16:13  白鹭·笙歌  阅读(52)  评论(0)    收藏  举报