题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1011

Starship Troopers

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 23806    Accepted Submission(s): 6342

Problem Description
You, the leader of Starship Troopers, are sent to destroy a base of the bugs. The base is built underground. It is actually a huge cavern, which consists of many rooms connected with tunnels. Each room is occupied by some bugs, and their brains hide in some of the rooms. Scientists have just developed a new weapon and want to experiment it on some brains. Your task is to destroy the whole base, and capture as many brains as possible.

To kill all the bugs is always easier than to capture their brains. A map is drawn for you, with all the rooms marked by the amount of bugs inside, and the possibility of containing a brain. The cavern's structure is like a tree in such a way that there is one unique path leading to each room from the entrance. To finish the battle as soon as possible, you do not want to wait for the troopers to clear a room before advancing to the next one, instead you have to leave some troopers at each room passed to fight all the bugs inside. The troopers never re-enter a room where they have visited before.

A starship trooper can fight against 20 bugs. Since you do not have enough troopers, you can only take some of the rooms and let the nerve gas do the rest of the job. At the mean time, you should maximize the possibility of capturing a brain. To simplify the problem, just maximize the sum of all the possibilities of containing brains for the taken rooms. Making such a plan is a difficult job. You need the help of a computer.

 
Input
The input contains several test cases. The first line of each test case contains two integers N (0 < N <= 100) and M (0 <= M <= 100), which are the number of rooms in the cavern and the number of starship troopers you have, respectively. The following N lines give the description of the rooms. Each line contains two non-negative integers -- the amount of bugs inside and the possibility of containing a brain, respectively. The next N - 1 lines give the description of tunnels. Each tunnel is described by two integers, which are the indices of the two rooms it connects. Rooms are numbered from 1 and room 1 is the entrance to the cavern.

The last test case is followed by two -1's.

 
Output
For each test case, print on a single line the maximum sum of all the possibilities of containing brains for the taken rooms.
 
Sample Input
5 10
50 10
40 10
40 20
65 30
70 30
1 2
1 3
2 4
2 5
1 1
20 7
-1 -1
 
Sample Output
50
7
 
Author
XU, Chuan
 
题意:
 

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

题意转自:https://blog.csdn.net/shuangde800/article/details/10069771

思路:典型的树形背包问题,这一题我用了两种方法写

特殊样例:

8 2
0 0
0 9
0 8
0 4
0 7
0 3
0 2
0 1
1 2
1 3
2 4
2 5
4 6
6 7
7 8 27

1.这种方法是我在一开始的代码一直wa然后看别人的思路改的,其中dp[i][j]表示以i节点构成的子树在有j个士兵的情况下最多可以获得的金钱数

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int val[];//价值
int wet[];//花费
bool vt[];//标记数组
struct{//链式前向星
int v,next;
}edge[];
int cnt;
int head[];
void add(int u,int v){//加边
edge[cnt].v=v;
edge[cnt].next=head[u];
head[u]=cnt++;
}
int dp[][];//dp[i][j]表示在以i个节点为根节点构成的子树中用j个士兵可以获得的最大金钱数
int n,m;
void dfs(int k){
vt[k]=true;
bool lg=true;
if(wet[k]==){//如果k是叶子节点并且它的花费为0,那么它的花费就要加一(因为你至少要派一个士兵去到这个房间才能获得该房间的金钱)
for(int i=head[k];i!=-;i=edge[i].next){
if(!vt[edge[i].v])
lg=false;
}
if(lg)
wet[k]++;
}
for(int i=;i<wet[k];i++)//初始化,要到达当前房间,需要的士兵数至少是wet[k],当士兵数小于wet[k]时可以获得的金钱数就为0
dp[k][i]=;
for(int i=wet[k];i<=m;i++){//当士兵数不少于当前房间的花费时,它可以获得当前房间的金钱
dp[k][i]=val[k];
}
for(int i=head[k];i!=-;i=edge[i].next){//看看是否可以往它下面的房间走使得得到的金钱数更多
if(!vt[edge[i].v]){
dfs(edge[i].v);//递归子节点
for(int j=m;j>=wet[k];j--){//尝试用子节点更新父节点
for(int l=wet[k];l<j;l++){
dp[k][j]=max(dp[k][j],dp[k][l]+dp[edge[i].v][j-l]);/*其中dp[k][]中保留着用上一个子节点更新父节点后的值,
相当于多重背包的上一样物品更新完后的背包,而当前子节点的值(dp[edge[i].v][])相当于当前物品*/
}
}
}
}
}
int main(){ while(scanf("%d%d",&n,&m)!=EOF&&(n!=-||m!=-)){
fill(head,head+,-);//初始化
fill(vt,vt+,false);
for(int i=;i<=n;i++){
scanf("%d%d",&wet[i],&val[i]);
wet[i]=wet[i]/+(wet[i]%!=);//转换一下花费,如果不足20个虫子也要派一个士兵
}
int u,v;
cnt=;
for(int i=;i<n;i++){//边是双向的
scanf("%d%d",&u,&v);
add(u,v);
add(v,u);
}
dfs();
if(m==)//特判一下,当你没有士兵时无论如何都得不到金钱
printf("0\n");
else
printf("%d\n",dp[][m]);
}
return ; }

2.这种方法是我最开始想到的,但是不知道为什么一直wa,然后找了半天才发现是自己没有充分理解 “如果当前节点花费为0且为叶子节点,那么它的花费要加一”这句话,我只考虑了完整的树的叶子节点花费为0的情况,而没有考虑到当我选出这棵树的部分的时候它叶子节点花费为0的情况.

在这种方法中,我的dp[i][j]表示的是以第i个节点为根节点时,向它的所有子树(不包括根节点)派遣j个士兵最多可以获得的金钱树

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int val[];
int wet[];
bool vt[];
struct{//链式前向星
int v,next;
}edge[];
int cnt;
int head[];
void add(int u,int v){
edge[cnt].v=v;
edge[cnt].next=head[u];
head[u]=cnt++;
}
int dp[][];//dp[i][j]表示向以i为根节点(不包括i)的所有子树中派遣j名士兵最多可以获得的金钱数
int n,m;
void dfs(int k){
for(int i=;i<=m;i++){//初始化全部为0
dp[k][i]=;
}
vt[k]=true;
for(int i=head[k];i!=-;i=edge[i].next){
if(!vt[edge[i].v]){
dfs(edge[i].v);
for(int j=m;j>=wet[edge[i].v]&&j>=;j--){
for(int l=;l<=j-wet[edge[i].v];l++){
//printf("QQQ%d %d",dp[k][j-l-wet[edge[i].v]-(l==0&&wet[edge[i].v]==0)],dp[edge[i].v][l]);
dp[k][j]=max(dp[k][j],dp[k][j-l-wet[edge[i].v]-(l==&&wet[edge[i].v]==)]+dp[edge[i].v][l]+val[edge[i].v]);
//你当前选择用子节点更新它的父节点时,就算当前子节点的花费为0你也至少要派一个士兵
//(l==0&&wet[edge[i].v]==0)的作用就是判断是否有叶节点的wet为0 ,l==0代表现在是用叶节点来更新
//printf("QQQ%d %d %d %d %d %d \n",k,edge[i].v,j,l,j-l-wet[edge[i].v]-(l==0&&wet[edge[i].v]==0),dp[k][j]);
}
//printf("qqqq%d %d %d\n",k,j,dp[k][j]);
}
}
}
}
int main(){
while(scanf("%d%d",&n,&m)!=EOF&&(n!=-||m!=-)){
fill(head,head+,-);//初始化
fill(vt,vt+,false);
for(int i=;i<=n;i++){
scanf("%d%d",&wet[i],&val[i]);
wet[i]=wet[i]/+(wet[i]%!=);//简化花费
}
int u,v;
cnt=;
for(int i=;i<n;i++){//边是双向的
scanf("%d%d",&u,&v);
add(u,v);
add(v,u);
}
dfs();
if(m==)//特判
printf("0\n");
else{
if(m>=wet[])//如果当前的士兵数大于根节点的花费数
dp[][m-wet[]]+=val[];
printf("%d\n",dp[][m-wet[]]);//如果小于的话不要else也是输出0
}
}
return ;
}
/*
8 2
0 0
0 9
0 8
0 4
0 7
0 3
0 2
0 1
1 2
1 3
2 4
2 5
4 6
6 7
7 8 27 */

当前我已经用节点5更新完了节点2,现在我用节点4来更新节点2,当更新dp[2][1]时,由于我dp的特殊性,当用dp[4][0]来更新dp[2][1]时,如果不注意上面的情况,就会发生

dp[2][1]=max(dp[2][1],dp[2][1]+dp[4][0]+val[4]),为什么呢,因为当前4节点的花费为零,如果我想只加上4节点时(即用dp[4][0]来更新时),会出现由于4节点花费为0而可以不用派士兵就可以得到它节点上的金钱的情况,而这种情况是不合法的,因为你至少要派一名士兵去4节点拿钱,而它不能走回头路,只能往下走。

hdu1011(树形背包)(提供一个特殊样例)的更多相关文章

  1. EXTJS 4 树形表格组件使用演示样例

    EXTJS 4 树形表格组件使用演示样例 一.总体效果图 version=1&modificationDate=1412058826000&api=v2" alt=" ...

  2. PHP初学者如何搭建环境,并在本地服务器(or云端服务器)运行自己的第一个PHP样例

    页面底部有PHP代码样例供测试使用. 1.PHP开发,你需要什么? 1)开发代码的工具,可以用IDE名字叫做phpDesigner.当然也可以临时用记事本代替,记得文件扩展名为.php 2)服务器(本 ...

  3. Spring Ajax一个简单样例

    配置不说了.要在前面helloworld的样例基础上弄. 相同在hello下新建ajax.jsp <%@ page language="java" contentType=& ...

  4. 【Xcode学C-1】怎样用Xcode练习C语言,并练习一个输出样例,以及重要的注意事项

    直接用Xcode学习C语言,为iOS开发打基础. (1)选择OS X >>> Application >>> Command Line Tool (2)输入产品名称 ...

  5. OpenCL多次循环执行内核的一个简单样例

    最近有不少朋友在多次循环执行OpenCL内核程序的时候碰到一些问题.由于对OpenCL初学者而言可能比较普遍,因此我这里给出一个清晰简单的demo来掩饰如何简单又高效地执行循环执行OpenCL内核. ...

  6. java文件夹相关操作 演示样例代码

    java文件夹相关操作 演示样例代码 package org.rui.io; import java.io.File; import java.io.FilenameFilter; import ja ...

  7. 10分钟理解Android数据库的创建与使用(附具体解释和演示样例代码)

    1.Android数据库简单介绍. Android系统的framework层集成了Sqlite3数据库.我们知道Sqlite3是一种轻量级的高效存储的数据库. Sqlite数据库具有以下长处: (1) ...

  8. Eureka 的 Application Service client的注冊以及执行演示样例

            Eureka 服务器架起来了(关于架设步骤參考博客<Linux 下 Eureka 服务器的部署>),如今怎样把我们要负载均衡的服务器(也就是从 Application Cl ...

  9. C#开发Unity游戏教程循环遍历做出推断及Unity游戏演示样例

    C#开发Unity游戏教程循环遍历做出推断及Unity游戏演示样例 Unity中循环遍历每一个数据,并做出推断 非常多时候.游戏在玩家做出推断以后.游戏程序会遍历玩家身上大量的所需数据,然后做出推断. ...

随机推荐

  1. ssh连接所生成的known_hosts出现的问题

    问题现场及解析 用OpenSSH的人都知ssh会把你每个你访问过计算机的公钥(public key)都记录在~/.ssh/known_hosts.当下次访问相同计算机时,OpenSSH会核对公钥.如果 ...

  2. mysql自增长主键,删除数据后,将主键顺序重新排序

    用数据库的时候,难免会删除数据,会发现设置的主键增长不是按照正常顺序排列,中间有断隔比如这样. 以我这个情况举例 处理方法的原理:删除原有的自增ID,重新建立新的自增ID. ALTER TABLE ` ...

  3. git删除远程分支,删除本地分支

    远程 # git push origin --delete duck - [deleted] duck 本地 # git branch -d duck 已删除分支 duck(曾为 dda11b8). ...

  4. 如何在开发过程中获取客户端的ip呢?

    在开发工作中,我们常常需要获取客户端的IP.一般获取客户端的IP地址的方法是:request.getRemoteAddr();但是在通过了Apache,Squid等反向代理软件就不能获取到客户端的真实 ...

  5. JAVA程序设计的第一次作业

    这是我第一次接触博客,刚开始用博客很生疏,感觉很麻烦,但是后来慢慢从老师那里了解到了许多博客可以带给我们的便利.通过博客,我们不仅可以记录自己从刚开始进入程序学习的懵懵懂懂到后来想要学,想深究,想探讨 ...

  6. 什么是卷积convolution

    定义 卷积是两个变量在某范围内相乘后求和的结果.如果卷积的变量是序列x(n)和h(n),则卷积的结果 , 其中星号*表示卷积. 当时序n=0时,序列h(-i)是h(i)的时序i取反的结果:时序取反使得 ...

  7. cron定时任务

    1.确认系统安装了cron rpm -aq | grep crontabs 2.认识cron时间格式 3.生成定时任务 crontab -e #进入任务命令编辑模式 30 7,12,20 * * * ...

  8. 大数据分析-excel常用技巧

    在用EXCEL制表时,经常要要用到填充,比如1到100行内容相同或引用公式,大多数人会用鼠标拖来拖去,例如: 在第一行的A1单元格右下方 鼠标指针 变 实心黑十字 向下拉或向右,向左拉 我想拉100行 ...

  9. 思科模拟器PacketTracer7--利用一台交换机将两台pc划分到不同vlan下

    实验2—3 实验内容:将同一交换机下的两台pc划分到不同vlan中 实验工具:思科模拟器PacketTracer7 使用设备:一台交换机,两台PC 实验步骤: 一.配置网络拓扑图 注:1.连线可选择闪 ...

  10. AutoLayout的坑

    本文投稿文章,作者:MangoMade(简书) AutoLayout非常强大也非常易用,可读性也很强,加上各种第三方AutoLayout库,让你布起局来犹如绷掉链子的狗!根本停不下来!以前的 1 la ...