hdu4044 依赖背包变形 好题!

由于不是求最大的可拦截的HP值,而是要将最小值最大化,那么就需要分配每个子树用的钱数以达到最小值最大化

第一步解决如何分配钱使得结点u的子树中用了j元钱后可以拦截的HP最大,这就是变形的分组(依赖)背包,即枚举m元钱,在子树v用t元钱,在v之前的子树用j-t元钱

  用Max[v][j]数组记录u的前v个结点花j元钱可以拦截的最大HP,Max[v][j]=max{min( dp[v][t] , Max[v-i][j-t] )},第一维v可以压缩掉

第二步考虑结点u上建立什么炮塔,用Max数组来求出dp[u][j]即可

/*
给定一棵树,m块钱,每个结点上可以建造k种(价格price,攻击力power)防御塔
求可以阻拦的最大血量 
dp[i][j]表示子树i中花费j元可以抵挡的怪物血量
 
*/
#include<bits/stdc++.h>
using namespace std;
#define maxn 1050
struct Edge{int to,nxt;}edge[maxn<<2];
struct node{int k,price[100],power[100];}p[maxn];
int n,m,head[maxn],tot,dp[maxn][210],flag[maxn];
void init(){
    tot=0;
    memset(head,-1,sizeof  head);
    memset(flag,0,sizeof flag);
}
void addedge(int u,int v){
    edge[tot].to=v;edge[tot].nxt=head[u];head[u]=tot++;
}
void dfs(int u,int pre){
    //init,每个点只能建立一个塔嘛 
    for(int j=m;j>=0;j--)
        for(int i=1;i<=p[u].k;i++)
            if(j>=p[u].price[i])
                dp[u][j]=max(dp[u][j],p[u].power[i]);
    
    if(flag[u]==1&&u!=1)return;//叶子节点退出 
    
    for(int i=head[u];i!=-1;i=edge[i].nxt){
        int v=edge[i].to;
        if(v!=pre)dfs(v,u);
    }    
    
    //Max[i][j]表示给前i个儿子花费j元时可以拦截的最大hp,可压成滚动数组 
    int Max[210];
    memset(Max,0x3f,sizeof Max); 
    for(int i=head[u];i!=-1;i=edge[i].nxt){
        int v=edge[i].to;
        if(v==pre)continue;
        for(int j=m;j>=0;j--){//给子树v之前的子树用j元 
            int maxx=0; 
            for(int t=0;t<=j;t++)//给子树v用t元 
                maxx=max(maxx,min(dp[v][t],Max[j-t]));//找出最优的分配方式 
            Max[j]=maxx;//记录 
        }
    }

    //最后Max数组表示给u的子树总共花i元可以拦截的最大HP 
    for(int j=m;j>=0;j--)
        for(int t=0;t<=j;t++)//给子树花费t,给自己花费j-t元 
            dp[u][j]=max(dp[u][j],dp[u][j-t]+Max[t]);
//printf("%d\n",u);
//for(int i=1;i<=m;i++)
//    printf("%d ",dp[1][i]);
}
int main(){
    int t,u,v;
    cin>>t;
    while(t--){
        init();
        scanf("%d",&n);
        for(int i=1;i<n;i++){
            scanf("%d%d",&u,&v);
            addedge(u,v);
            addedge(v,u);
            flag[u]++;flag[v]++;
        }
        scanf("%d",&m);
        for(int i=1;i<=n;i++){
            scanf("%d",&p[i].k);
            for(int j=1;j<=p[i].k;j++)
                scanf("%d%d",&p[i].price[j],&p[i].power[j]);
        }
        memset(dp,0,sizeof dp);
        dfs(1,0);
        printf("%d\n",dp[1][m]);
    }    
} 

猜你喜欢

转载自www.cnblogs.com/zsben991126/p/10348262.html