hdu 1011 Starship Troopers (树形背包dp)



本文出自   http://blog.csdn.net/shuangde800



题目链接 : hdu-1011

 

题意

有n个洞穴编号为1~n,洞穴间有通道,形成了一个n-1条边的树, 洞穴的入口即根节点是1。
每个洞穴有x只bugs,并有价值y的金子,全部消灭完一个洞穴的虫子,就可以获得这个洞穴的y个金子.
现在要派m个战士去找金子,从入口进入。每次只有消灭完当前洞穴的所有虫子,才可以选择进入下一个洞穴。
一个战士可以消灭20只虫子,如果要杀死x只虫子,那么要x/20向上取整即(x+19)/20个战士。
如果要获得某个洞穴的金子,必须留下足够杀死所有虫子的战士数量, 即(x+19)/20个战士,然后这些留下战士就不能再去其它洞穴
其他战士可以继续走去其它洞穴,可以选择分组去不同的洞穴。
战士只能往洞穴深处走,不能走回头路
问最多能获得多少金子?



思路

这题一开始没有理解好题意,所以WA了多次,理解好题意就不难了。
我们可以知道每个节点的花费cost(i) = (x(i)+19)/20。
那么,
f(i, j):表示i子树,用j个战士最多可以获得的价值。
如果i有s个儿子节点,那么就形成了s组的物品,对每组物品进行分组背包。
每一组可以选择派1,2...j-cost(i)个战士去,为什么最多是j-cost(i)?因为还要留下cost(i)个战士消灭当前洞穴的虫子。
这样就可以得到状态转移了:

f(i, j) = max { max{ f(i, j-k) + f(v, k) | 1<=k<=j-cost(i) } |  v是i的儿子节点 }

这题要特别注意的是,如果是叶子节点,并且叶子节点的花费为0,那么要让他的花费变为1,因为必须派一个战士走向叶子节点才可以获得金子.


代码

/**=====================================================
 *   This is a solution for ACM/ICPC problem
 *
 *   @source      : hdu-1011 Starship Troopers
 *   @description : 树形背包dp
 *   @author      : shuangde
 *   @blog        : blog.csdn.net/shuangde800
 *   @email       : zengshuangde@gmail.com
 *   Copyright (C) 2013/08/18 20:27 All rights reserved. 
 *======================================================*/
#include 
    
    
     
     
#include 
     
     
      
      
#include 
      
      
       
       
#include 
       
       
        
        
#include 
        
        
          #include 
         
           #include 
          
            #define MP make_pair using namespace std; typedef pair 
           
             PII; typedef long long int64; const int INF = 0x3f3f3f3f; const double PI = acos(-1.0); const int MAXN = 110; int n, m; struct Node { int num,cost, val; }room[MAXN]; int f[MAXN][MAXN]; bool vis[MAXN]; vector 
            
              adj[MAXN]; void dfs(int u) { vis[u] = true; memset(f[u], 0, sizeof(f[u])); // 如果是叶子节点,并且花费为0时,要变成1 if (!room[u].cost && adj[u].size()==1 && u!=1) room[u].cost = 1; // 初始化 for (int i = room[u].cost; i <= m; ++i) f[u][i] = room[u].val; for (int i = 0; i < adj[u].size(); ++i) { int v = adj[u][i]; if (vis[v]) continue; dfs(v); for (int s = m; s >= room[u].cost; --s) { for (int j = 1; s-j >= room[u].cost; ++j) f[u][s] = max(f[u][s], f[u][s-j] + f[v][j]); } } } int main(){ while (~scanf("%d %d", &n, &m) && n+m != -2) { for (int i = 1; i <= n; ++i) adj[i].clear(); for (int i = 1; i <= n; ++i) { int x; scanf("%d%d", &x, &room[i].val); room[i].cost = x/20 + (x%20!=0); } for (int i = 0; i < n - 1; ++i) { int u, v; scanf("%d %d", &u, &v); adj[u].push_back(v); adj[v].push_back(u); } if (m==0) { puts("0"); continue; } memset(vis, 0, sizeof(vis)); vis[1] = true; dfs(1); printf("%d\n", f[1][m]); } return 0; } 
             
            
           
          
        
       
       
      
      
     
     
    
    






评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值