HDU 1011 Starship Troopers星河战队(AC代码)树形dp

这个没有考虑过多东西的代码也是AC了。

 1 #include <iostream>
 2 #include <vector>
 3 #define limit 110
 4 using namespace std;
 5
 6 vector<int> vect[limit];
 7 int n,m;
 8 bool vis[limit];
 9 int bug[limit],pos[limit];
10 int dp[limit][limit];
11
12 int max(int a,int b){return a>b? a:b;}
13 void dfs(int p,int people)
14 {
15     int i,j,k,temp,leave;
16     temp=(bug[p]+19)/20; //攻此房间需要temp个人手
17
18     if(people>=temp)
19     {
20         for(i=temp;i<=people;i++)
21             dp[p][i]=pos[p];    //该房间p的不同人手i所对应的dp值
22         vis[p]=1;    //置为浏览过
23         leave=people-temp;
24         for(i=0;i<(int)vect[p].size();i++)    //与此结点相连的所有结点都要遍历
25         {
26             if(vis[vect[p][i]]) continue;    //若遍历过的,则跳过
27             dfs(vect[p][i],leave);
28             for(j=people;j>=temp;j--)
29             {
30                 for(k=1; k<=j-temp; k++)
31                     dp[p][j]=max( dp[p][j] , dp[p][j-k]+dp[vect[p][i]][k] );
32             }
33         }
34     }
35 }
36 int main()
37 {
38     int i;
39     int u,v;
40     while(scanf("%d%d",&n,&m),n!=-1||m!=-1)    //洞穴数目 人手
41     {
42         for(i=0; i<n; i++)
43             vect[i].clear();
44         memset(dp,0,sizeof(dp));    //清内存
45         memset(vis,0,sizeof(vis));    //清内存
46         for(i=0; i<n; i++)
47             scanf("%d %d",&bug[i],&pos[i]);    //房间里的bug数、可能性
48
49         for(i=1; i<n; i++)            //连通情况
50         {
51             scanf("%d%d",&u,&v);
52             vect[u-1].push_back(v-1);
53             vect[v-1].push_back(u-1);
54         }
55         if(m==0) {printf("0\n");continue;}
56         dfs(0,m);
57         printf("%d\n",dp[0][m]);
58     }
59     return 0;
60 }

1011

题意

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

要特别注意的是,如果是叶子节点,并且叶子节点的花费为0,那么要让他的花费变为1,因为必须派一个战士走向叶子节点才可以获得金子,但此战士不能往上走了,只能往下走,也许再碰到一个bug数为0的洞就可以捡金子了,要是一路往下都是bug为0,一路捡下去都行。

下面给个考虑多一点的AC代码。

 1 #include <iostream>
 2 #include <vector>
 3 #define limit 110
 4 using namespace std;
 5
 6 vector<int> vect[limit];
 7 int n,m;
 8 bool vis[limit];
 9 int bug[limit],pos[limit];
10 int dp[limit][limit];
11
12 int max(int a,int b){return a>b? a:b;}
13 void dfs(int p,int people)
14 {
15     int i,j,k,temp,leave;
16     temp=(bug[p]+19)/20; //攻此房间需要temp个人手
17
18     if(people>=temp)    //假如people=0,temp=0,那应该如何?没人手,又想捡人头
19     {
20         if(people==0&&temp==0)    return;    //应该这么解决!都不可能派给此洞人手,下面的行为还有必要吗?
21         for(i=temp;i<=people;i++)
22             dp[p][i]=pos[p];
23         vis[p]=1;    //置为浏览过
24         leave=people-temp;
25         for(i=0;i<(int)vect[p].size();i++)    //与此结点相连的所有结点都要遍历
26         {
27             if(vis[vect[p][i]]) continue;    //若浏览过的,则跳过
28             dfs(vect[p][i],leave);
29             for(j=people;j>=temp;j--)
30             {
31                 for(k=1; k<=j-temp; k++)
32                     dp[p][j]=max( dp[p][j] , dp[p][j-k]+dp[vect[p][i]][k] );
33             }
34         }
35     }
36 }
37 int main()
38 {
39     int i;
40     int u,v;
41     while(scanf("%d%d",&n,&m),n!=-1||m!=-1)
42     {
43         for(i=0; i<n; i++)
44             vect[i].clear();
45         memset(dp,0,sizeof(dp));    //清内存
46     memset(vis,0,sizeof(vis));    //清内存
47         for(i=0; i<n; i++)
48             scanf("%d %d",&bug[i],&pos[i]);    //房间里的bug数、金子
49
50         for(i=1; i<n; i++)            //连通情况
51         {
52             scanf("%d%d",&u,&v);
53             vect[u-1].push_back(v-1);
54         vect[v-1].push_back(u-1);
55         }
56         if(m==0) {printf("0\n");continue;}
57         dfs(0,m);
58         printf("%d\n",dp[0][m]);
59     }
60     return 0;
61 }

1011

下面是我想了很久却不明白之处:在接收“边”的时候,把每条边设为无向边,即把每个结点设想为带若干通向其孩子的边+带一条通向父亲的边。此题的遍历方式是从上往下,即从根结点开始,那么在遍历一个结点x的时候,其vector容器中vect[x]中就会有一个父亲结点的编号,刚从父亲递归下来的,父亲肯定被置为遍历过了,却还需要在vect[x]中当遇到遍历过的结点时就跳过。此举不是多余吗?vis数组的意义在哪?

我认为应该是这样的代码:

 1 #include <iostream>
 2 #include <vector>
 3 #define limit 110
 4 using namespace std;
 5
 6 vector<int> vect[limit];
 7 int n,m;
 8 //bool vis[limit];
 9 int bug[limit],pos[limit];
10 int dp[limit][limit];
11
12 int max(int a,int b){return a>b? a:b;}
13 void dfs(int p,int people)
14 {
15     int i,j,k,temp,leave;
16     temp=(bug[p]+19)/20; //攻此房间需要temp个人手
17
18     if(people>=temp)    //假如people=0,temp=0,那应该如何?没人手,又想捡人头
19     {
20         if(people==0&&temp==0)    return;    //应该这么解决!都不可能派给此洞人手,下面的行为还有必要吗?
21         for(i=temp;i<=people;i++)
22             dp[p][i]=pos[p];
23         //vis[p]=1;    //置为浏览过
24         leave=people-temp;
25         for(i=0;i<(int)vect[p].size();i++)
26         {
27         //    if(vis[vect[p][i]]) continue;    //若浏览过的,则跳过
28             dfs(vect[p][i],leave);
29             for(j=people;j>=temp;j--)
30             {
31                 for(k=1; k<=j-temp; k++)
32                     dp[p][j]=max( dp[p][j] , dp[p][j-k]+dp[vect[p][i]][k] );
33             }
34         }
35     }
36 }
37 int main()
38 {
39     int i;
40     int u,v;
41     while(scanf("%d%d",&n,&m),n!=-1||m!=-1)
42     {
43         for(i=0; i<n; i++)
44             vect[i].clear();
45         memset(dp,0,sizeof(dp));    //清内存
46     //    memset(vis,0,sizeof(vis));
47         for(i=0; i<n; i++)
48             scanf("%d %d",&bug[i],&pos[i]);
49
50         for(i=1; i<n; i++)
51         {
52             scanf("%d%d",&u,&v);
53             vect[u-1].push_back(v-1);
54         //    vect[v-1].push_back(u-1);
55         }
56         if(m==0) {printf("0\n");continue;}
57         dfs(0,m);
58         printf("%d\n",dp[0][m]);
59     }
60     return 0;
61 }

1011

但是其结果是WA,这实在令人难接受啊!这就好像你吃饭时特意拿个碗去装了菜,到最后这盘菜都没动过一口,还得洗碗!

时间: 2024-10-14 20:18:59

HDU 1011 Starship Troopers星河战队(AC代码)树形dp的相关文章

hdu 1011 Starship Troopers (依赖背包 树形dp)

题目: 链接:点击打开链接 题意: n个房间组成一棵树,你有m个战队,从1号房间开始依次clear每个房间,在每个房间需要花费的战队个数是bugs/20,得到的价值是the possibility of capturing a brain,求最大的价值. 算法: 树形dp,有依赖的背包问题.(依次clear每个房间) 思路: 状态转移dp[i][j]表示根结点为i时(房间i)花费j个战队能够得到的最大价值(捕捉到一个brain最大的可能值).递归求出每个根结点处的最大值,最后dp[1][m]就是

HDU 1011 Starship Troopers(树形DP)

Starship Troopers Time Limit : 10000/5000ms (Java/Other)   Memory Limit : 65536/32768K (Java/Other) Total Submission(s) : 62   Accepted Submission(s) : 12 Font: Times New Roman | Verdana | Georgia Font Size: ← → Problem Description You, the leader of

HDU 1011 Starship Troopers(树形dp+背包)

Starship Troopers Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 13109    Accepted Submission(s): 3562 Problem Description You, the leader of Starship Troopers, are sent to destroy a base of

hdu 1011 Starship Troopers(树形DP入门)

Starship Troopers Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 17498    Accepted Submission(s): 4644 Problem Description You, the leader of Starship Troopers, are sent to destroy a base of t

hdu 1011 Starship Troopers DP

Starship Troopers Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 11929    Accepted Submission(s): 3295 Problem Description You, the leader of Starship Troopers, are sent to destroy a base of

hdu 1011 Starship Troopers 树形背包

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1011 题意:有n个洞,每个洞有若干虫子和脑子,洞之间相连形成一棵树.你有m个士兵,一个士兵可以打10只虫子,士兵留下后就不能走了.从一号洞开始打,求获得最多的脑子. 树形背包.如果你在某一个洞里,有m个士兵.你可以选择派遣k个士兵去攻打与洞相连的某个子树.可以先用递归算出子树的情况.这类似于背包九讲的泛化背包. 定义 考虑这样一种物品,它并没有固定的费用和价值,而是它的价值随着你分配给它的费用而变化

HDU 1011 Starship Troopers 树形DP 有坑点

本来是一道很水的树形DP题 设dp[i][j]表示,带着j个人去攻打以节点i为根的子树的最大收益 结果wa了一整晚 原因: 坑点1: 即使这个节点里面没有守卫,你如果想获得这个节点的收益,你还是必须派一个人去这个节点,不然谁帮你去拿收益? 坑点2: 题目说是从节点1开始攻打,然后我就以为给出的数据都是以1为根节点的,就没有加双向边 不过, 最后我加了双向边,然后还是wa了 又找了很久,最后发现是前向星的数组开小了 注意:数组开小了可能会返回wa,tle,re等等等等 1 #include<cst

hdu 1011 Starship Troopers

树上的背包 #include <iostream> #include <cstdio> #include <algorithm> #include <vector> #include <queue> #include <cmath> #include <cstring> using namespace std; const int MAXN = 110; int n, m; struct Node { int num,co

树形dp/hdu 1011 Starship Troopers

题意 有n个房子,1号为起点房子.每个房子里会消耗一定的士兵来获取一定的价值.现在有m个士兵,求问可以获得的最大价值 注意:走过的房子不能再走 注意2:若要消灭这个房子的bugs,必须全部消灭 分析 典型的树形dp,01背包,因为每个房子里要么全杀死bugs,要么一个不动,只有取或不取两种状态 设f[i][j]表示以i为根节点,消耗j个士兵所能获得的最大价值 则f[i][j]=max(f[son[i]][k] + f[i][j-k]); 答案为f[1][m] Accepted Code 1 /*