博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
hdu 1011 Starship Troopers (树形背包dp)
阅读量:4072 次
发布时间:2019-05-25

本文共 2495 字,大约阅读时间需要 8 分钟。

本文出自   

题目链接 :

 

题意

有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; }
你可能感兴趣的文章
将file文件内容转成字符串
查看>>
循环队列---数据结构和算法
查看>>
优先级队列-数据结构和算法
查看>>
链接点--数据结构和算法
查看>>
servlet中请求转发(forword)与重定向(sendredirect)的区别
查看>>
Spring4的IoC和DI的区别
查看>>
springcloud 的eureka服务注册demo
查看>>
eureka-client.properties文件配置
查看>>
MODULE_DEVICE_TABLE的理解
查看>>
platform_device与platform_driver
查看>>
platform_driver平台驱动注册和注销过程(下)
查看>>
.net强制退出主窗口的方法——Application.Exit()方法和Environment.Exit(0)方法
查看>>
c# 如何调用win8自带的屏幕键盘(非osk.exe)
查看>>
build/envsetup.sh 简介
查看>>
Android framework中修改或者添加资源无变化或编译不通过问题详解
查看>>
linux怎么切换到root里面?
查看>>
linux串口操作及设置详解
查看>>
安装alien,DEB与RPM互换
查看>>
编译Android4.0源码时常见错误及解决办法
查看>>
Android 源码编译make的错误处理
查看>>