(JZ1046)2018.07.07【2018提高组】模拟B组 2.【GDOI2005】寻宝之旅

【GDOI2005】寻宝之旅【难】 (Standard IO)

Description

探险队长凯因意外的弄到了一份黑暗森林的藏宝图,于是,探险队一行人便踏上了寻宝之旅,去寻找传说中的宝藏。
藏宝点分布在黑暗森林的各处,每个点有一个值,表示藏宝的价值。它们之间由一些小路相连,小路不会形成环,即两个宝藏点之间有且只有一条通路。探险队从其中的一点出发,每次他们可以留一个人在此点开采宝藏,也可以不留,然后其余的人可以分成若干队向这一点相邻的点走去。需要注意的是,如果他们把队伍分成两队或两队以上,就必须留一个人在当前点,提供联络和通讯,当然这个人也可以一边开采此地的宝藏。并且,为了节约时间,队伍在前往开采宝藏的过程中是不会走回头路的。现在你作为队长的助理,已经提供了这幅藏宝图,请你算出探险队所能开采的最大宝藏的价值。

Input

第一行有两个正整数n(1<=n<=100),表示藏宝点的个数,m(1<=m=100)表示探险队的人数。
第二行是n个不超过100的正整数,分别表示1到n每个点的宝藏价值。
接下来的n-1行,每行两个数,x和y(1<=x,y<=n,x<>y),表示藏宝点x,y之间有一条路,数据保证不会有重复的路出现。
假设一开始探险队在点1处。

Output

一个整数,表示探险队所能获得最大的宝藏价值。

Sample Input

5 3
1 3 7 2 8
1 2
2 3
1 4
4 5

Sample Output

16

Data Constraint

题解:

    本题是树形动态规划(关于树形dp的模板,可以参照一下这里:传送门)
    设f[i][j]表示在以i为根结点的子树上,留j个人的最大宝藏价值
   有题目可得:队员可以挖或跳过某个点,可得 f[i][j]=max(f[i][j],f[i][j-1-k]+f[to[i]][k]+v[i])。但我们不知道f[i][j-1-k]是否加过v[i],如果有的话就是重复了。
   那么我们可以设g[i][j]表示在以i为根结点的子树上,留j个人,且i处不留人的最大宝藏价值。便可将状态转移方程转化成f[i][j]=max(f[to[i]][k]+g[i][j-k-1]+a[i])。在维护完f[i][j]以后将 g[i][j]维护一下即可

#include<iostream>
#include<cstdio>
#define N 110
using namespace std;

int n,m,x,y,cnt,v[N],g[N][N],f[N][N],to[N*2],next[N*2],head[N],k[N];

inline void add(int x , int y)
{
    cnt++;
    to[cnt]=y,next[cnt]=head[x],head[x]=cnt;
}

void dfs(int x , int y)
{
    f[x][1]=v[x];
    int z=head[x];
    while (z != 0)
     {

        if(to[z] != y){
             dfs(to[z],x);
             for (int i=1;i<=m;i++) k[i]=max(f[x][i],f[to[z]][i]);
             for (int i=1;i<=m;i++)
                for (int j=1;j<i;j++)
                    k[i]=max(k[i],g[x][i-j-1]+f[to[z]][j]+v[x]);
             for (int i=1;i<=m;i++) f[x][i]=k[i];   
             for (int i=1;i<=m;i++) k[i]=max(f[to[z]][i],g[x][i]);
             for (int i=1;i<=m;i++)
                for (int j=1;j<i;j++)
                     k[i]=max(k[i],g[x][i-j]+f[to[z]][j]);
             for (int i=1;i<=m;i++) g[x][i]=k[i];    
        }
        z=next[z];
     }      
}
int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++) scanf("%d",&v[i]);
    for (int i=1;i<n;i++)
    {
        scanf("%d%d",&x,&y);
        add(x,y); add(y,x); 
    }
    dfs(1,0);
    printf("%d",f[1][m]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值