树形DP——hiho 1055

  • 题目链接:
    https://hihocoder.com/problemset/problem/1055

  • 分析:
    给出一棵N个节点的树,一共N-1条边,每个节点都有其价值,要求从中选出互相连接的M个节点(包括节点1)使得其价值和最大。

  • 题解:
    设dp[i][j]为第i个节点下的选择j个节点时(包括第i个节点)的最大价值。

    我们先把每一个dp[i][1]都初始化每个节点自己的价值,表示单独选择i节点时候得到的最大价值。然后从节点1开始DFS(题意要求必须包括节点1),每搜到它下面一个节点,再以那个节点进行DFS。DFS返回这个节点下的子节点数目。然后更新DP数组:

int dfs(int u, int m) //计算节点个数,同时更新dp数组
{
    int sons = 1;  //子节点个数初始化为1,即自己本身
    int num;
    for(int i=head[u]; i!=-1; i=edge[i].next)
    {
        int v = edge[i].v; //取出相连的第一个节点
        num = dfs(v, m-1); //计算v节点下一共有多少个子节点,同时也会更新dp[v]下面的最大值。
        for(int j=m;j>=1;j--)
        {
            for(int k=1; k<=num && k<j; k++)
            {
                dp[u][j] = MAX( dp[u][j], dp[v][k]+dp[u][j-k] );
                //j表示u节点下一共取多少个节点,k表示v节点下一共取多少个节点,这样遍历一遍更新出最大的dp[u][j]值。
            }
        }
        sons += num;
    }
    return sons;//返回节点数
}
  • AC代码:
    这里写图片描述
/*************************************************************************
    > File Name: 1055.cpp
    > Author: Akira 
    > Mail: qaq.febr2.qaq@gmail.com 
    > Created Time: 2016年10月07日 星期五 14时21分56秒
 ************************************************************************/

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <stack>
#include <map>
#include <cmath>
#include <vector>
#include <set>
#include <list>
#include <ctime>
typedef long long LL;
typedef unsigned long long ULL;
typedef long double LD;
#define MST(a,b) memset(a,b,sizeof(a))
#define CLR(a) MST(a,0)
#define Sqr(a) ((a)*(a))
using namespace std;

#define MaxN 110
#define MaxM MaxN*2
#define INF 0x3f3f3f3f
#define bug cout<<88888888<<endl;
#define MIN(x,y) (x<y?x:y)
#define MAX(x,y) (x>y?x:y)

int N, M;
int dp[MaxN][MaxN];

struct Edge
{
    int u,v;
    int next;
}edge[MaxM];
int head[MaxN];
int cont;

void init()
{
    CLR(dp);
    MST(head, -1);
    cont = 0;
}

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

int dfs(int u, int m)
{
    int sons = 1;
    int num;

    for(int i=head[u]; i!=-1; i=edge[i].next)
    {
        int v = edge[i].v;
        num = dfs(v, m-1);
        for(int j=m;j>=1;j--)
        {
            for(int k=1; k<=num && k<j; k++)
            {
                dp[u][j] = MAX( dp[u][j], dp[v][k]+dp[u][j-k] );
            }
        }
        sons += num;
    }
    return sons;
}

int main()
{
    while(~scanf("%d%d", &N, &M))
    {
        init();
        for(int i=1;i<=N;i++)
        {
            scanf("%d", &dp[i][1]);
        }
        int a, b;
        for(int i=1;i<N;i++)
        {
            scanf("%d%d", &a, &b);
            add(a, b);
        }
        dfs(1, M);
        cout << dp[1][M] << endl;
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值