HDU1011Starship Troopers(DP+DFS)深度优先搜索+动态规划

来源:互联网 发布:系统工程师 软件开发 编辑:程序博客网 时间:2024/05/20 07:57

Starship Troopers

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


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 1050 1040 1040 2065 3070 301 21 32 42 51 120 7-1 -1
 

Sample Output
507
 

Author
XU, Chuan
 

Source
ZJCPC2004
 

Recommend
JGShining
 

思路:题目大致讲的是:星际骑兵要抢洞穴的宝藏,宝藏为brain,但是宝藏有bug在守卫,所以,骑兵需要击败bugs才能拿走宝藏,1个骑兵可以击败20个bugs,所以,如果当前i洞穴bugs数目为bug[i],则骑兵需要cost=ceil(bug[i]/20)才能拿走宝藏,我们的目标是拿走尽可能多的宝藏,解出最大宝藏数量.并且洞穴分布(图)是树状.
看到树状,容易想到DFS,
每经过一个结点,需要付出代价并且获得收益,这是背包问题,而且是树状背包.用DP思想,DP需要找到状态转移式子.假定sodier在结点parent的收益是Gain[parent][sodier],则状态可以转移到Gain[son][send],其中send表示的是派到子结点的骑兵数量,则有Gain[parent][sodier]=max{Gain[parent][sodier-send],Gain[son][send]},其中send的取值是[1,sodier-send],用一个循环比较[1,sodier-send],这里起点要是1,派出的士兵至少要1个才能获得宝藏.
代码风格就是DFS+DP了.有几个版本:
solution 1:
//重要的状态转换式,DP//GetBrain(parent, Soldier) = max{ GetBrain(parent, Soldier-send) + GetBrain(son, send) } //1<=send<=Soldier-cost(parent),son是parent的儿子节点 }//Gain[i][j]表示(i,j,Gain[i][j])=j个士兵在i节点上获得了Gain[i][j]宝藏#include<iostream>#include<algorithm>#include<string>#include<vector>using namespace std;const int N = 110;int n,SodierSum,Gain[N][N],bug[N],brain[N];vector<int> g[N];/*邻接表,g[i][0]存放的是与i邻接的结点数目,从1开始存放结点2 2-> 52 1-> 5-> 3-> 42 2-> 43 2-> 5-> 33 4-> 1-> 2 */bool visited[N];void init(){    memset(visited,false,sizeof(visited));    memset(Gain,0,sizeof(Gain));    for(int i=0;i<=n;++i)        g[i].clear();}void dfs(int root){    visited[root]=true;    int size=g[root].size();//节点代价,需要派出的士兵数量,向上取整    int cost=(bug[root]+19)/20;//如果士兵无法击败bugs,当然无法获得宝藏    if( SodierSum < cost ) return ;//从cost数起,因为sodier的数量必须>=cost才能获得宝藏//只要士兵数>=cost,所获宝藏是一样的,都是brain[root]    for(int i=cost;i<=SodierSum;++i)            Gain[root][i]=brain[root];    for(int i=0;i<size;++i)    {        int son=g[root][i];//找邻接点,用二维数组存图,实际上类似链式存储        if(visited[son])continue;        dfs(son); //dfs+dp思想        for(int sodier=SodierSum;sodier>=cost;--sodier)//根节sodier数        {            for(int send=1;send<=sodier-cost;send++)//派出去的士兵最多sodier-cost,因为至少要留下cost人在父节点//状态转移,核心if( sodier - send >= cost) //留守的士兵应该>=cost,题目要求.Gain[root][sodier]=max(Gain[root][sodier],Gain[root][sodier-send]+Gain[son][send]);        }    }}int main(){    int x,y;    while(scanf("%d %d",&n,&SodierSum)==2)    {        if(n==-1 && SodierSum==-1)            break;        for(int i=1;i<=n;++i)            scanf("%d %d",&bug[i],&brain[i]);        init();        for(int root=1;root<n;root++)        {            scanf("%d %d",&x,&y);            g[x].push_back(y);            g[y].push_back(x);        }        if(SodierSum==0)//没有人就不可能获得brain        {            printf("0\n");            continue;        }else{dfs(1);cout << Gain[1][SodierSum] << endl;}           }    return 0;}

solution 2:
#include<iostream>using namespace std;const int MAXN=110;int N,M;struct Node{int number;//number:该结点的bug数int p;//p:该结点的possible;};Node node[MAXN];//记录结点 int dp[MAXN][MAXN];//DP,dp[i][j]表示根结点为i时,用掉j个士兵获得的最大值 int adj[MAXN][MAXN];//存树 。adj[i][j]:结点为i,adj[i][0]表示该结点所连接的结点(父结点和子结点一共)个数//类似链式存储/*3 1-> 2-> 35 3-> 4-> 5-> 6-> 76 9-> 4-> 3-> 2-> 5-> 6*///adj[i][j](j>=1)表示结点编号,j=结点个数+1bool vis[MAXN];//访问标记 int max(int a, int b){return a>b ? a:b;}void dfs(int root)//DFS,求该结点及其分支所能获得的最大possible{vis[root]=true;//已经访问 int cost=(node[root].number+19)/20;//获得该结点需要的士兵数目 //士兵数只要能击败bugs,则brain可以全获for(int i=cost;i<=M;i++) dp[root][i]=node[root].p;//用掉i个士兵获得的possiblefor(int i=1;i<=adj[root][0];i++)//adj[root][0]表示root结点的邻接节点数{int u=adj[root][i];//典型的链式存储形式if(vis[u]) continue;//跳过已访问的dfs(u);//求该子结点的最大possiblefor(int j=M;j>=cost;j--)//父节点的士兵数{for(int k=1;k<=M-cost;k++)//派出的士兵数k,至少要派出1个士兵才能获得下面的宝藏,从1开始,注意!!{//状态转移式,重点!DP思想if( j-k >= cost ) //留守士兵数应该>=costdp[root][j]=max(dp[root][j],dp[root][j-k]+dp[u][k]);}    }    }    }  int main(){int b,e;while(cin >> N >> M){if(N==-1&&M==-1) break;memset(vis,false,sizeof(vis));memset(dp,0,sizeof(dp));memset(adj,0,sizeof(adj));for(int i=1;i<=N;i++)cin >> node[i].number >>node[i].p;for(int i=1;i<N;i++)//存图 {cin >> b >> e;//连接结点b和eadj[b][0]++; //链表+1adj[b][adj[b][0]]=e;//在b链表末尾存下一个结点eadj[e][0]++; //链表+1adj[e][adj[e][0]]=b;//互连}if(M==0)//这个必需要,有代价为0的房间,M=0则无法获得 cout << 0 << endl;else{dfs(1);cout << dp[1][M] << endl;}} return 0;} 

solution 3:

#include<iostream>#include<stdio.h>#include<stdlib.h>#include<math.h>#include<algorithm> #include<string.h>using namespace std;int map[110][110];int bug[110];int bra[110];int vis[110];int dp[110][110];int n,m;//dp[i][j]表示在i洞派出j个骑兵获取的brain值//动态规划转移式//dp[i][j]=max{dp[x][j],dp[x][j-k]+dp[i][k]};void  dfs(int x,int pre)//记录上一次visited的结点,如果这次visit的和上一次相同,则继续下一个.{int tr=(bug[x]+19)/20;for(int i=m;i>=tr;i--)//初始化收益dp[x][i]=bra[x];  //士兵数只要能击败bugs,则brain可以全获for(int i=1;i<=n;++i){if(map[x][i]&&pre!=i){dfs(i,x);for(int j=m;j>=tr;j--){for(int k=1;k<=j-tr;k++){dp[x][j]=max(dp[x][j],dp[x][j-k]+dp[i][k]);     }      }}      }     } int main(){while(cin>>n>>m && (n!=-1||m!=-1)){for(int i=1;i<=n;i++)cin>>bug[i]>>bra[i];int a,b;memset(map,0,sizeof(map));memset(dp,0,sizeof(dp));for(int i=1;i<n;i++){cin>>a>>b;map[a][b]=1;//建邻接矩阵map[b][a]=1;     }                if(m==0){cout<<0<<endl;continue;      }dfs(1,-1);cout<<dp[1][m]<<endl;}return 0;} 


原创粉丝点击