http://www.bnuoj.com/bnuoj/problem_show.php?pid=29358
这个题目的话,初看就想到要用背包,一个包一个包的装,装好两个包的话,就可以直接判断了。
当然中间还有剪枝的部分,代码中有很好的体现,这个怎么说呢,代码写了很多,但是是为了节省时间,比较划来的。
当然这个问题最重要的不是剪枝,而是背包算法的运用:如何标记第一个背包已经使用过的物品。
为了标记,我觉得使用二维dp数组比一维数组好,但是标记后的处理也十分重要,详见代码。
此处本人还耍了一点小聪明,标记的话,第二次读的时候n和整个数组都变了,突发奇想,把标记了的物品体积赋值为0.这样就不会影响其它的背包了。
代码如下:
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 #define M 450 6 #define mem0(f) memset(f,0,sizeof(f)) 7 int t; 8 int n,c; 9 int v[M]; 10 int dp[M][M];//二维背包数组 11 int vis[M][M];//表示第i个物品在构成j时是否被选用 12 int s; 13 int ok; 14 int biaoji[M]; 15 int main() 16 { 17 scanf("%d",&t); 18 for(int tt=1;tt<=t;tt++) 19 { 20 ok=1; 21 scanf("%d%d",&n,&c); 22 s=0; 23 mem0(v); 24 25 for(int i=1;i<=n;i++)//用二维数组的话,一定要从1开始存入 26 { 27 scanf("%d",&v[i]); 28 if(v[i]>c) 29 { 30 ok=0; 31 } 32 s+=v[i]; 33 } 34 if(s>3*c)ok=0; 35 if(ok==0) 36 { 37 printf("Case %d: No ",tt); 38 continue; 39 } 40 int p=2; 41 // mem0(dp); 42 mem0(biaoji); 43 while(p--) 44 { 45 mem0(vis); 46 mem0(dp); 47 for(int i=1;i<=n;i++) 48 { 49 //if(biaoji[i])continue; 50 for(int k=c;k>=0;k--) 51 { 52 // 53 if(k>=v[i]) 54 { 55 if(dp[i-1][k]>dp[i-1][k-v[i]]+v[i]) 56 { 57 dp[i][k]=dp[i-1][k]; 58 vis[i][k]=0; 59 } 60 else 61 { 62 dp[i][k]=dp[i-1][k-v[i]]+v[i]; 63 vis[i][k]=1; 64 } 65 } 66 else 67 { 68 dp[i][k]=dp[i-1][k]; 69 vis[i][k]=0; 70 } 71 } 72 } 73 // printf("tttttttttt%d ",dp[n][c]); 74 //第一个背包 75 76 if(p==1&&dp[n][c]<(double)s/3)//剪枝 77 { 78 printf("Case %d: No ",tt); 79 break; 80 } 81 s-=dp[n][c]; 82 if(p==0&&dp[n][c]<(double)s/2)//剪枝 83 { 84 printf("Case %d: No ",tt); 85 break; 86 } 87 88 if(p==0) 89 { 90 s<=c; 91 printf("Case %d: Yes ",tt); 92 break; 93 } 94 if(s<=0) 95 { 96 printf("Case %d: Yes ",tt); 97 break; 98 } 99 int pt=0,vv=c; 100 for(int i=n;i>=1;i--) 101 { 102 if(!vis[i][vv]) 103 { 104 biaoji[i]=0; 105 //v[i]=0; 106 } 107 else 108 { 109 biaoji[i]=1;vv=vv-v[i]; 110 v[i]=0;//把装过了的值赋为0 111 } 112 113 } 114 } 115 } 116 return 0; 117 }