题解报告:hdu 1171 Big Event in HDU(多重背包)

Problem Description

Nowadays, we all know that Computer College is the biggest department in HDU. But, maybe you don't know that Computer College had ever been split into Computer College and Software College in 2002.
The splitting is absolutely a big event in HDU! At the same time, it is a trouble thing too. All facilities must go halves. First, all facilities are assessed, and two facilities are thought to be same if they have the same value. It is assumed that there is N (0<N<1000) kinds of facilities (different value, different kinds).

Input

Input contains multiple test cases. Each test case starts with a number N (0 < N <= 50 -- the total number of different facilities). The next N lines contain an integer V (0<V<=50 --value of facility) and an integer M (0<M<=100 --corresponding number of the facilities) each. You can assume that all V are different.
A test case starting with a negative integer terminates input and this test case is not to be processed.

Output

For each case, print one line containing two integers A and B which denote the value of Computer College and Software College will get respectively. A and B should be as equal as possible. At the same time, you should guarantee that A is not less than B.

Sample Input

2
10 1
20 1
3
10 1
20 2
30 1
-1

Sample Output

20 10
40 40
解题思路:典型的多重背包问题,由于题目给的时限比较多,果断转化成01背包暴力过了233。题意是:尽量使得A,B两者分得的价值接近相等(两者价值总和为所有设施设备的总价值),并且A分得的价值不小于B分得的价值,那么问题就可以转化成不超过总价值的一半(视为背包的容量△(已经是确定值))下,挑选若干件物品装入背包后,使得总价值最大,即为B得到的最大价值,A得到的价值为sum-dp[sum/2]。最坏的时间复杂度大约为6e8级别,可见测试数据挺水的。
AC代码一(717ms):
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int n,v,m,num,sum,vi[5005],dp[125005];//最大价值的一半:50*100*50/2=125000,数组开大一点即可
 4 int main(){
 5     while(~scanf("%d",&n)&&n>=0){
 6         memset(vi,0,sizeof(vi));
 7         memset(dp,0,sizeof(dp));
 8         num=sum=0;//sum记录总价,num记录物品的总数
 9         while(n--){
10             scanf("%d%d",&v,&m);
11             while(m--){//有m个价值相等的物品
12                 vi[num++]=v;//把m个物品看成m件不同的物品,多重背包转化成01背包
13                 sum+=v;//累加价值
14             }
15         }
16         for(int i=0;i<num;++i)
17             for(int j=sum/2;j>=vi[i];--j)
18                 dp[j]=max(dp[j],dp[j-vi[i]]+vi[i]);
19         printf("%d %d
",sum-dp[sum/2],dp[sum/2]);//dp[sum/2]比较小即为B,因为求解过程是让max尽量靠近sum/2
20     }
21     return 0;
22 }

AC代码二(78ms):

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int t,W,n,tol,value[55],num[55],dp[125005];
 4 void ZeroOnePack(int w,int v){//01背包
 5     for(int j=W;j>=w;--j)
 6         dp[j]=max(dp[j],dp[j-w]+v);
 7 }
 8 void CompletePack(int w,int v){//完全背包
 9     for(int j=w;j<=W;++j)
10         dp[j]=max(dp[j],dp[j-w]+v);
11 }
12 void MultiplePack(int w,int v,int num){//多重背包
13     if(w*num>=W)CompletePack(w,v);
14     else{
15         for(int k=1;k<=num;k<<=1){//二进制思想
16             ZeroOnePack(w*k,v*k);
17             num-=k;
18         }
19         if(num>0)ZeroOnePack(w*num,v*num);
20     }
21 }
22 int main(){
23     while(~scanf("%d",&n)&&n>=0){
24         memset(dp,0,sizeof(dp));tol=W=0;
25         for(int i=1;i<=n;++i){
26             scanf("%d%d",&value[i],&num[i]);
27             tol+=value[i]*num[i];
28         }
29         W=tol/2;
30         for(int i=1;i<=n;++i)
31             MultiplePack(value[i],value[i],num[i]);
32         printf("%d %d
",tol-dp[W],dp[W]);
33     }
34     return 0;
35 }

AC代码三(62ms):单调队列优化

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int W,n,tol,val[55],num[55],dp[125005];
 4 struct node{
 5     int k,v;
 6     node(int x,int y):k(x),v(y){}
 7 };
 8 deque<node> dq;
 9 void SingleDeque(int w,int v,int cnt){
10     for(int r=0;r<w;++r){//r=j%w
11         dq.clear();
12         for(int t=0;t*w+r<=W;++t){//t=j/w
13             int tmp=dp[t*w+r]-t*v;
14             while(!dq.empty()&&tmp>=dq.back().v)dq.pop_back();
15             dq.push_back(node(t,tmp));
16             while(!dq.empty()&&(t-cnt>dq.front().k))dq.pop_front();
17             dp[t*w+r]=dq.front().v+t*v;
18         }
19     }
20 }
21 int main(){
22     while(~scanf("%d",&n)&&n>=0){
23         tol=W=0;memset(dp,0,sizeof(dp));
24         for(int i=1;i<=n;++i)
25             scanf("%d%d",&val[i],&num[i]),tol+=val[i]*num[i];
26         W=tol/2;
27         for(int i=1;i<=n;i++){
28             num[i]=min(num[i],W/val[i]);
29             SingleDeque(val[i],val[i],num[i]);
30         }
31         printf("%d %d
",tol-dp[W],dp[W]);
32     }
33     return 0;
34 }
原文地址:https://www.cnblogs.com/acgoto/p/9522601.html