【hdu】1011 Starship Troopers【树形背包】

【题意】:有一颗树,树的节点编号为1-n,1为根节点,每个节点上会有x个敌人,y个宝藏,现在你从1号节点出发,率领m个友方单位,只有攻占了父节点才能继续攻占子节点,如果你在一个节点的兵力*20>=敌人数,你就可以得到这个节点中的宝藏,并且这些士兵就永久驻扎在这个节点了,并且所有士兵不能走回头路(注意这个条件非常重要,我在这里WA了2次,这个条件就意味着,就算每个节点的敌人数量都是0,也至少需要一个士兵才能获得宝藏,并且这个士兵只能选择树上的一条链走到底)

【题解】:主要是题目条件比较坑,其他的就是裸的树形背包,在树上进行背包,思想很简单dp[i][j]代表以i节点为根的子树派出兵力j所能得到最多的宝藏数  

递推式dp[u][j]=max(dp[u][j],dp[u][j-k]+dp[v][k])

其中u为父节点 j为给以u为根的子树分配的兵力数 v为子节点 k为分配给以v为根节点的子树的兵力  

其中只有攻占了父节点才能继续攻占子节点这个条件,只要在对每个点进行背包的时候强制留下w[u]个士兵即可(w[u]为攻占u节点所需要的士兵数)

其他细节看代码

#include<set>
#include<map>
#include<cmath>
#include<stack>
#include<queue>
#include<bitset>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define PB push_back
#define MP make_pair
#define ll __int64
#define MS(a,b) memset(a,b,sizeof(a))
#define LL (rt<<1)
#define RR (rt<<1|1)
#define lson l,mid,LL
#define rson mid+1,r,RR
#define pii pair<int,int>
using namespace std;
const int MAXN=1e2+10;
const int inf=0x3f3f3f3f;
const int mod=1e9+7;
int dp[MAXN][MAXN];
int w[MAXN],val[MAXN];
int m;
vector<int>G[MAXN];
void dfs(int u,int fa)
{
    for(int j=w[u];j<=m;j++)dp[u][j]=val[u];//初始化 因为必须要先攻下父节点才能往子节点走 所有父节点是必须要取得
    for(int i=0;i<G[u].size();i++){
        int v=G[u][i];
        if(v==fa)continue;
        dfs(v,u);
        for(int j=m;j>=w[u];j--)//枚举在u及其子树的派兵量 倒着枚举是和背包一样 防止重复使用
            for(int k=1;k<=j-w[u];k++)//枚举分配给v及其子树的士兵量 因为至少有一个士兵才能有得到大脑 不管敌人数量是不是0 
                dp[u][j]=max(dp[u][j],dp[u][j-k]+dp[v][k]);//上限是j-w[u]是因为u节点必须要攻占 
    }
}
int main()
{//freopen("C:\\Users\\Administrator\\Desktop\\input.txt","r",stdin);
    int n;
    while(scanf("%d%d",&n,&m),n!=-1||m!=-1){
        MS(dp,0);
        for(int i=1;i<=n;i++){
            int tmp;
            scanf("%d%d",&tmp,&val[i]);
            w[i]=(tmp+19)/20;//算攻占一个洞穴需要几个士兵
            G[i].clear();
        }
        for(int i=1;i<n;i++){
            int u,v;
            scanf("%d%d",&u,&v);
            G[u].PB(v);G[v].PB(u);
        }
        if(m==0){printf("0\n");continue;}//至少要有人才能拿到大脑
        dfs(1,-1);
        printf("%d\n",dp[1][m]);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值