hdu 1171 Big Event in HDU

  这题就是多重背包,分成尽可能相等的两部分,题目的数据规模比较小,有个不大的坑,以负数来作结束输入,而不是-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 }
View Code

  然后,还发现有人竟然把它转化为了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 }
View Code

  最后,不得不说下,这题的数据还真是够弱的,AC了也没有什么兴奋的感觉。

原文地址:https://www.cnblogs.com/Newdawn/p/4314430.html