这题就是多重背包,分成尽可能相等的两部分,题目的数据规模比较小,有个不大的坑,以负数来作结束输入,而不是-1。我还是参考《挑战》的多重背包的做法,以dp[i][j]表示前 i 中物品构成价值 j 时物品 i 还剩下多少个,递推方程也就模板化了。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 const int INF= 0x3fffffff; 6 7 int dp[150003], v[52], num[52]; 8 9 int main(){ 10 int n,i,j; 11 while(~scanf("%d",&n),n>=0){ 12 memset(dp,-1,sizeof(dp)); 13 dp[0]= 0; 14 int sum= 0; 15 for(i=1; i<=n; ++i){ 16 scanf("%d%d",v+i,num+i); 17 sum+= v[i]*num[i]; 18 } 19 int aver= sum/2; 20 for(i=1; i<=n; ++i) 21 for(j=0; j<=aver; ++j) 22 if(dp[j]!= -1) dp[j]= num[i]; 23 else if(j<v[i] || dp[j-v[i]]<=0) dp[j]= -1; 24 else dp[j]= dp[j-v[i]]-1; 25 for(j=aver; ;--j) 26 if(dp[j]!= -1) break; 27 printf("%d %d ",sum-j,j); 28 } 29 return 0; 30 }
提交后看到有人用贪心的方法来做,竟然AC了,0ms,确实很高效,实际上是错的,因为选择了那些眼前看是合适的(最后的结果未必含有)后无法逆转,不像 dp用数组记录状态不断动态更新,有回转的余地,可见杭电这题的数据有多弱。但也是因为我用贪心写了下,无意中从swap函数参数的引用传递搞明白一点东西,实参必须是实实际际的变量,不能是表达式(除非const引用,常量引用)。 // 这个代码就不建议看了,以免弄混乱
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 6 int obj[5002],v,num; 7 8 int main() 9 { 10 int b=5, c=3, d; //为验证swap中参数的引用传递 11 int &r = (d = b-c); //引用必须绑定一个实实际际的变量,直接 b-c赋给 &r的话会报错 12 const int &r2 = b-c; // const引用为常量引用,没有问题 13 14 int n,i; 15 while(~scanf("%d",&n),n>=0){ 16 int sum= 0, len= 0; 17 for(i=1; i<=n; ++i){ 18 scanf("%d%d",&v,&num); 19 sum+= v*num; 20 while(num--) obj[len++]= v; 21 } 22 int half= sum>>1; 23 if(sum&1) ++half; 24 25 sort(obj,obj+len); 26 int tmp= 0; 27 28 //从大的开始一直贪心,其实这种做法是错的,因为加了那些眼前看是合适的(最优结果未必含有)后无法逆转, 29 //不像 dp用数组记录状态不断动态更新,有回转的余地,可见杭电这题的数据有多弱 30 for(i= len-1; i>=0; --i) 31 if(tmp+obj[i]<=half) tmp+= obj[i]; 32 33 int tmp2= sum-tmp; 34 if(tmp2 < tmp) swap(tmp2,tmp); //swap是引用传递,所以实参不能为表达式,必须为实实际际的变量 35 printf("%d %d ",tmp2,tmp); 36 } 37 return 0; 38 }
然后,还发现有人竟然把它转化为了01背包来做,就是把同类物品摊开来,使其成为所有物品的数量都是1的问题,于是便是最简单的01背包了,也能AC,只是500+ms,感觉没这必要,只是让我再次感受到了dp的力量,确实非同小可:
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 6 int dp[150003],obj[5003], v,num; 7 8 int main(){ 9 int n,i,j; 10 while(~scanf("%d",&n),n>=0){ 11 int sum= 0, len= 0; 12 for(i=1; i<=n; ++i){ 13 scanf("%d%d",&v,&num); 14 sum+= v*num; 15 while(num--) obj[++len]= v; 16 } 17 int half= sum/2; 18 memset(dp,0,sizeof(dp)); 19 for(i=1; i<=len; ++i) 20 for(j= half; j>=obj[i]; --j) 21 dp[j]= max(dp[j],dp[j-obj[i]]+obj[i]); 22 printf("%d %d ",sum-dp[half],dp[half]); 23 } 24 return 0; 25 }
最后,不得不说下,这题的数据还真是够弱的,AC了也没有什么兴奋的感觉。