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;
}

浙公网安备 33010602011771号