<bzoj4027: [HEOI2015]兔子与樱花> (树形DP)

题意:一颗有根有向树 每个点有权值 定义每个节点的权值加上与他直接相连的儿子的个数不能超过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;
}
View Code

猜你喜欢

转载自www.cnblogs.com/lwqq3/p/9022490.html