HDU 1011 Starship Troopers 树形+背包dp
http://acm.hdu.edu.cn/showproblem.php?pid=1011
题意:每个节点有两个值bug和brain,当清扫该节点的所有bug时就得到brain值,只有当父节点被清空时,才可以清扫它 的子节点,而清扫需要一定的人员。给定M个人员,N个结点的树,求最大brain和
这看起来是一道非常简单的背包dp
但是
写完提交wa之后,我发现这道题,并不简单!因为他的题意并不是完全和我找到的题意一样(我承认我语文很差劲所以随便找个题解看题意。。),题目中要求每一个人是不能往回走的,所以节点bug值为0时也不能随意收集brain,一个人员只能收集一条链。当然0个人的时候不管怎么样都收集不到brain直接特判。
这个问题可以通过一个非常之骚的操作解决,操作如下
f[a][b]代表以a点为根的子树布置b个人能得到的最大brain的和
1 for(int k=m;k>=bu[x];k--){ 2 for(int j=1;j+k<=m;j++){//注意这个地方从1开始 3 f[x][k+j]=max(f[x][k+j],f[x][k]+f[y][j]); 4 } 5 }
j从1开始循环完美避免了不给下面的路分配人却得到brain值的事情发生,然后就可以ac了。。。
但是并没有ac。。为什么呢。。。因为我,没有把写好的清空数据函数放到主程序里。。。
然后我wa了8次。。。引以为戒。
代码:
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #include<algorithm> 5 #include<cmath> 6 using namespace std; 7 const int maxn=110; 8 const double eps=1e-8; 9 const int modn=45989; 10 int n,m; 11 struct nod{ 12 int next,y; 13 }e[maxn*2]; 14 int bu[maxn]={},bra[maxn]={},head[maxn]={},tot=0; 15 bool vis[maxn]={}; 16 int f[maxn][maxn]={}; 17 void init(int x,int y){ 18 e[++tot].y=y; 19 e[tot].next=head[x]; 20 head[x]=tot; 21 } 22 void dfs(int x){ 23 vis[x]=1; 24 int y; 25 for(int i=bu[x];i<=m;i++){ 26 f[x][i]=bra[x]; 27 } 28 for(int i=head[x];i;i=e[i].next){ 29 y=e[i].y; 30 if(!vis[y]){ 31 dfs(y); 32 for(int k=m;k>=bu[x];k--){ 33 for(int j=1;j+k<=m;j++){ 34 f[x][k+j]=max(f[x][k+j],f[x][k]+f[y][j]); 35 } 36 } 37 } 38 } 39 } 40 void yu(){ 41 tot=0;memset(f,0,sizeof(f)); 42 memset(head,0,sizeof(head)); 43 memset(vis,0,sizeof(vis)); 44 } 45 int main(){ 46 for(;;){ 47 yu(); 48 scanf("%d%d",&n,&m); 49 if(n==-1||m==-1){ 50 break; 51 } 52 for(int i=1;i<=n;i++){ 53 scanf("%d%d",&bu[i],&bra[i]); 54 bu[i]=(bu[i]+19)/20; 55 } 56 int x,y; 57 for(int i=1;i<n;i++){ 58 scanf("%d%d",&x,&y); 59 init(x,y);init(y,x); 60 } 61 if(m==0){ 62 printf("0\n"); 63 continue; 64 } 65 dfs(1); 66 printf("%d\n",f[1][m]); 67 } 68 return 0; 69 }