andre_joy

导航

hdu 1561

地址:http://acm.hdu.edu.cn/showproblem.php?pid=1561

题意:要攻克一些城堡,有些城堡可以直接攻克,有些必须要先攻克一个指定城堡才能攻克他。每个城堡里面有一些宝物,求攻克m个城堡的最大宝物数。

mark:该题用到一个很实用的技巧就是题目给定的是一片森林,那么我们为这些根节点建立一个共同的根节点,那么就变成一棵树了。

    常规树形dp解法。dp[i][j]代表第i个节点为根节点的节点个数为j的最大价值。则dp[v][1] = w[v];(v是叶子节点)

    dp[i][j] = max(dp[i][j], dp[i][k]+dp[son][j-k]);

    这里用到一个小的剪枝,那就是dp[i][j]的j最大值是根据不断更新的情况定的,可以减少一些无用状态的转移。

代码:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

const int N = 210;
int n,m;
int head[N],son[N];
int w[N];
int dp[N][N];

int max(int a, int b) {return a > b ? a : b;}

int dfs(int fa)
{
    int i,j,k;
    int tot = 1;
    dp[fa][1] = w[fa];
    for(i = son[fa]; i != -1; i = head[i])
    {
        tot += dfs(i);  //剪枝,每次只用循环到当前能达到的最大值。 
        for(j = tot; j >= 1; j--)
            for(k = 1; k+j <= tot+1; k++)
                dp[fa][j+k] = max(dp[fa][j+k], dp[fa][j]+dp[i][k]);
    }
    return tot;
}

int main()
{
    int i,j,k;
    while(scanf("%d%d", &n, &m), n+m)
    {
        memset(son, -1, sizeof(son));
        w[0] = 0;
        for(i = 1; i <= n; i++) 
        {
            scanf("%d%d", &j, w+i);
            head[i] = son[j];
            son[j] = i;
        }
        memset(dp, 0, sizeof(dp));
        dfs(0);
        printf("%d\n", dp[0][m+1]);
    }
    return 0;
}

posted on 2012-10-10 22:15  andre_joy  阅读(824)  评论(0编辑  收藏  举报