HDU 1561 The more, The Better(树形DP+01背包)

版权声明:没人会转的( ̄▽ ̄) https://blog.csdn.net/j2_o2/article/details/84111592
题目链接
题意(这好像是中文题,不会别的语言=_=||)
思路

我们以0为根节点向外建树,以前置点为起点,该点为终点,该点价值为线的权值。
由于每个结点只有一个父节点,每点权值等价两点之间线上的权值。
第一组样例可以这样建图
在这里插入图片描述
dp[i][j] 表示 i结点选j个的最大权值。
由于可以在任意子节点选部分数列然后组合而成,比如一个复杂的树,某点选择四个,可以由许多不同子节点选择方式组合而成。这里可以使用01背包优化。
每次先dfs子节点出它的答案。回溯时01背包一次,对于父节点所有的j,dp[u][j],遍历该儿子所有可能(选k个)对其进行更新,dp[u][j] = max(dp[u][j], dp[u][k]+dp[v][j-k])

代码
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;

int first[205], tot;

struct Edge
{
    int v, nxt, w;
}e[205];

void add(int u, int v, int w)
{
    e[tot].w = w;
    e[tot].v = v;
    e[tot].nxt = first[u];
    first[u] = tot++;
}

int dp[205][205], n, m;

void dfs(int u)
{
    dp[u][0] = 0;
    for(int i = first[u]; ~i; i = e[i].nxt)
    {
        int v = e[i].v;
        dp[v][1] = e[i].w;
        dfs(v);
        for(int j = m; j > 0; --j)
        {
            for(int k = j; k > min(u-1,0); --k) // 因为根节点是可以不选,dp[0][0]合法,其余最少选1dp[][1]
            {
//                printf("**%d %d %d - %d %d %d **\n",u,k,dp[u][k], v,j-k,dp[v][j-k]);
                dp[u][j] = max(dp[u][j], dp[u][k]+dp[v][j-k]);
            }
//        printf("---- %d %d %d\n",u,j,dp[u][j]);
        }
    }
}

int main()
{
    int a, b;
    while(scanf("%d%d",&n,&m), n)
    {
        tot = 0;
        memset(first,-1,sizeof(first));
        memset(dp,~0x3f,sizeof(dp));
        for(int i = 1; i <= n; ++i)
        {
            scanf("%d%d",&a,&b);
            add(a,i,b);
        }
        dfs(0);
        printf("%d\n",dp[0][m]);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/j2_o2/article/details/84111592