[树形DP]寻宝之旅

题目描述

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

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

分析

范围小,一棵树,树形DP无误
容易想到fi,j表示以当前节点i为根的子树里有j个探险者所能获取的最大价值
然后为了防止重复加某个点的宝藏值,加一维0..1表示有无采过
然后状态转移方程很简单的

#include <iostream>
#include <cstdio>
#define rep(i,a,b) for (i=a;i<=b;i++)
const int N=101;
using namespace std;
int n,m;
int a[N];
int f[N][N][2];
struct Edge {
    int u,v,nx;
}g[2*N];
int cnt,list[N];
bool b[N];

void Addedge(int u,int v) {
    g[++cnt].u=u;g[cnt].v=v;g[cnt].nx=list[u];list[u]=cnt;
    g[++cnt].u=v;g[cnt].v=u;g[cnt].nx=list[v];list[v]=cnt;
}

void Dfs(int x) {
    int lf;
    b[x]=1;f[x][1][0]=a[x];
    for (int i=list[x];i;i=g[i].nx)
    if (!b[g[i].v]) {
        Dfs(g[i].v);
        int j,k;
        for (j=m;j>=1;j--) {
            lf=max(f[x][j][0],f[g[i].v][j][0]);
            rep(k,1,j-1)
            lf=max(lf,f[x][j-k-1][1]+f[g[i].v][k][0]+a[x]);
            f[x][j][0]=lf;
        }
        for (j=m;j>=1;j--) {
            lf=max(f[x][j][1],f[g[i].v][j][0]);
            rep(k,1,j)
            lf=max(lf,f[x][j-k][1]+f[g[i].v][k][0]);
            f[x][j][1]=lf;
        }
    }
}

int main() {
    int i;
    scanf("%d%d",&n,&m);
    rep(i,1,n)
    scanf("%d",&a[i]);
    rep(i,1,n-1) {
        int u,v;
        scanf("%d%d",&u,&v);
        Addedge(u,v);
    }
    Dfs(1);
    printf("%d",f[1][m][0]);
}

猜你喜欢

转载自blog.csdn.net/SSL_QYH0Ice/article/details/80951637
今日推荐