HDOJ 1561 The more, The Better (树形DP)

Problem Description
ACboy很喜欢玩一种战略游戏,在一个地图上,有N座城堡,每座城堡都有一定的宝物,在每次游戏中ACboy允许攻克M个城堡并获得里面的宝物。但由于地理位置原因,有些城堡不能直接攻克,要攻克这些城堡必须先攻克其他某一个特定的城堡。你能帮ACboy算出要获得尽量多的宝物应该攻克哪M个城堡吗?
 

Input
每个测试实例首先包括2个整数,N,M.(1 <= M <= N <= 200);在接下来的N行里,每行包括2个整数,a,b. 在第 i 行,a 代表要攻克第 i 个城堡必须先攻克第 a 个城堡,如果 a = 0 则代表可以直接攻克第 i 个城堡。b 代表第 i 个城堡的宝物数量, b >= 0。当N = 0, M = 0输入结束。
 

Output
对于每个测试实例,输出一个整数,代表ACboy攻克M个城堡所获得的最多宝物的数量。
 

Sample Input
3 2
0 1
0 2
0 3
7 4
2 2
0 1
0 4
2 1
7 1
7 6
2 2
0 0
 

Sample Output
5
13


思路:树形DP, dp[i][j]表示当节点为i时攻克j个点时获得的财富, 根节点为0开始。要注意的是根节点不用攻克自己完全选择它的子树就可, 而非根节点至少要攻克它自己本身,然后在从子树中选择攻克的数量,


code:

#include <cstdio>
#include <cstring>
#define max(a, b) (a)>(b)? (a):(b)
using namespace std;
struct node
{
    int v, next;
}edge[202];
int cnt = 0, n = 0, m = 0, value[202], head[202], uead[202], dp[202][202];

void addedge(int u, int v)
{
    edge[cnt].v = v;
    edge[cnt].next = head[u];
    head[u] = cnt++;
}

void dfs(int cur)
{
    int i = 0, j = 0, t = 0, p = 0, v = 0, max = 0;
    dp[cur][0] = 0;
    for(p = head[cur]; p != -1; p = edge[p].next)
    {
        v = edge[p].v;
        dfs(v);
        for(i = m; i>0; i--)
        {
            max = dp[cur][i];
            if(cur == 0) t = i;//由于点0不用攻克, 所以当节点为0时只选它的子树
            else t = i-1;//不是根节点, 要攻克它自身
            for(j = 0; j<=t; j++)
                max = max(max, dp[v][j]+dp[cur][i-j]);
//            if(cur == 0) j = 0;  //与上面转移方程一样
//            else j = 1;
//            for(; j<=i; j++)
//                max = max(max, dp[v][i-j]+dp[cur][j]);
            dp[cur][i] = max;
        }
    }
    for(i = 1; i<=m; i++)//加上自己本身
        dp[cur][i] += value[cur];
}

int main()
{
    int i = 0, u = 0, ans = 0, cost = 0, n = 0, cnt2 = 0, num[202];
    while(scanf("%d %d", &n, &m), n+m)
    {
        value[0] = 0;
        memset(head, -1, sizeof(head));
        memset(edge, 0, sizeof(edge));
        memset(dp, 0, sizeof(dp));
        cnt = cnt2 = 0;
        for(i = 0; i<n; i++)
        {
            scanf("%d %d", &u, &cost);
            value[i+1] = cost;
            addedge(u, i+1);
        }
        dfs(0);
        printf("%d\n", dp[0][m]);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值