题意:一颗有根有向树 每个点有权值 定义每个节点的权值加上与他直接相连的儿子的个数不能超过m个
可以删除一个点 使得这个点的权值赋给他父亲 把他的儿子也都连在他的父亲上
问在满足定义的情况下最多能删除多少点
题解:其实就有点递归+贪心的思想
每个点的总价值为点权+儿子数
对于每个点如果他有儿子和孙子 且删了他的孙子后 他的儿子不能删了的话
显然是删除他的孙子比较优 虽然对答案的贡献都是1
但是这个点的权值会小 因为没有删他儿子所传递过来的点权
所以可以贪心的从叶子节点如果能删就删
同样 对于同一个节点的多个儿子 dfs下去回溯时儿子已经处理好了
显然是在不超过m的情况下对于总价值越小的儿子优先删
#include <stdio.h> #include <algorithm> #include <iostream> #include <vector> using namespace std; int n, m; int q[2000005]; int son[2000005]; int head[2000005]; struct node { int to, nex; }E[2000005]; struct no { int id, val; }que[2000005]; int ans; bool cmp(no A, no B) { return A.val < B.val; } void dfs(int x) { int cn = 0; int c = head[x]; for(int i = c; i; i = E[i].nex) { int v = E[i].to; dfs(v); //que[++cn].id = v; 这样写肯定不对 cn变量xjb变了 //que[cn].val = son[v] + q[v]; } for(int i = c; i; i = E[i].nex) { int v = E[i].to; que[++cn].id = v; que[cn].val = son[v] + q[v]; } sort(que + 1, que + 1 + cn, cmp); for(int i = 1; i <= cn; i++) { if(que[i].val + son[x] + q[x] - 1 <= m) { ans++; son[x] += son[que[i].id] - 1; q[x] += q[que[i].id]; } else break; } return; } int main() { int cnt = 0; ans = 0; scanf("%d%d", &n, &m); for(int i = 0; i < n; i++) scanf("%d", &q[i]); for(int i = 0; i < n; i++) { int x, y; scanf("%d", &x); son[i] = x; for(int j = 1; j <= x; j++) { scanf("%d", &y); E[++cnt].to = y; E[cnt].nex = head[i]; head[i] = cnt; } } dfs(0); printf("%d\n", ans); return 0; }
原文地址:https://www.cnblogs.com/lwqq3/p/9022490.html
时间: 2024-11-05 18:44:58