2020ICPC南京 M.Monster Hunter(树形背包)

题意:

给定n个顶点的树,点1为根,
每个点都是一个怪物,第i个怪物有a(i)的血量,
如果你要消灭第x个怪物,需要花费a(x)+sum{a(v)}的代价,其中v是与x相连的儿子。

现在你可以进行k次操作,一次操作可以直接删掉某个怪物而不需要代价,
问对于k=[0,n],消灭所有怪物需要的最少代价是多少。

数据范围:n<=2e3

解法:

显然存在树形依赖,
令d[0/1][i][j]表示:当前点是否保留,以i为根的子树保留j个点的最小花费.
树形dp一下即可,需要用size优化将复杂度变为O(n^2).

转移方程见代码

code:

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int maxm=2e3+5;
vector<int>g[maxm];
int d[2][maxm][maxm];
int sz[maxm];
int a[maxm];
int n;
void dfs(int x){
    
    
    sz[x]=1;
    for(int i=0;i<=n;i++){
    
    
        d[0][x][i]=d[1][x][i]=1e18;
    }
    d[1][x][1]=a[x];//保留
    d[0][x][0]=0;//删掉
    for(int v:g[x]){
    
    
        dfs(v);
        for(int i=sz[x];i>=0;i--){
    
    
            for(int j=0;j<=sz[v];j++){
    
    
                d[0][x][i+j]=min(d[0][x][i+j],d[0][x][i]+d[0][v][j]);
                d[0][x][i+j]=min(d[0][x][i+j],d[0][x][i]+d[1][v][j]);
                d[1][x][i+j]=min(d[1][x][i+j],d[1][x][i]+d[0][v][j]);
                d[1][x][i+j]=min(d[1][x][i+j],d[1][x][i]+a[v]+d[1][v][j]);//xv都不删,则需要多花费a[v].
            }
        }
        sz[x]+=sz[v];
    }
}
void solve(){
    
    
    cin>>n;
    for(int i=1;i<=n;i++)g[i].clear();
    for(int i=2;i<=n;i++){
    
    
        int fa;cin>>fa;
        g[fa].push_back(i);
    }
    for(int i=1;i<=n;i++){
    
    
        cin>>a[i];
    }
    dfs(1);
    for(int i=0;i<=n;i++){
    
    
        cout<<min(d[0][1][n-i],d[1][1][n-i])<<' ';
    }
    cout<<endl;
}
signed main(){
    
    
    ios::sync_with_stdio(0);
    int T;cin>>T;
    while(T--){
    
    
        solve();
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_44178736/article/details/112723419