【FZU2178】礼物分配

题意

  在双胞胎兄弟Eric与R.W的生日会上,他们共收到了N个礼物,生日过后他们决定分配这N个礼物(numv+numw=N)。对于每个礼物他们俩有着各自心中的价值vi和wi,他们要求各自分到的礼物数目|numv-numw|<=1,并且各自所衡量的礼物价值的差值|sumv-sumw|尽可能小,现在他们想知道最小的差值是多少。

分析

  这是中途相遇法的模板题

  每个礼物要么属于Eric,要么属于R.W,所以如果暴力的话是2^30,显然会超。

  使用中途相遇法可以将复杂度降到2^15左右,很是神奇。

 1. 先将N个礼物分成两份,第一份有n/2个礼物,第二份有n-n/2个礼物。

 2. 然后枚举第一份中有哪些属于Eric,哪些属于R.W。cnt来记录第一份中Eric的礼物数目,sum1是第一份中Eric的礼物价值和,sum2是R.W的礼物价值和。然后用一个vector,把每个sum1-sum2都加到下标为cnt的vector中。

 3.  用类似的方法,枚举第二份中哪些属于Eric,哪些属于R.W。sum1,sum2,cnt的含义相同。然后在下标为n-n/2-cnt的vector中找和这个sum1-sum2相加最小的值,然后判断是否要更新ans。

  就是这样,用这个题来学中途相遇法了。。。步骤3中的小细节还是比较神奇的。

  下面是代码

  

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <iostream>
 4 #include <algorithm>
 5 #include <vector>
 6 #include <map>
 7 
 8 using namespace std;
 9 typedef long long LL;
10 
11 const int maxn=35;
12 const int INF=2147000000;
13 int T,n;
14 int v[maxn],w[maxn];
15 vector<int>V[maxn];
16 
17 int main(){
18     scanf("%d",&T);
19     for(int t=1;t<=T;t++){
20         scanf("%d",&n);
21         for(int i=0;i<=n;i++)V[i].clear();
22         for(int i=1;i<=n;i++)
23             scanf("%d",&v[i]);
24         for(int i=1;i<=n;i++)
25             scanf("%d",&w[i]);
26         int n1,n2;
27         n1=n/2,n2=n-n1;
28         int cnt;
29         LL sum1,sum2;
30         for(int i=0;i<(1<<n1);i++){
31             cnt=0,sum1=0,sum2=0;
32             for(int j=0;j<n1;j++){
33                 if(i&(1<<j)){
34                     cnt++;
35                     sum1+=v[j+1];
36                 }else
37                     sum2+=w[j+1];
38             }
39             V[cnt].push_back(sum1-sum2);
40         }
41 
42         for(int i=0;i<=n1;i++){
43             sort(V[i].begin(),V[i].end());
44             V[i].erase(unique(V[i].begin(),V[i].end()),V[i].end());
45         }
46 
47 
48         int ans=INF;
49         for(int i=0;i<(1<<n2);i++){
50             cnt=0,sum1=0,sum2=0;
51             for(int j=0;j<n2;j++){
52                 if(i&(1<<j)){
53                     cnt++;
54                     sum1+=v[n1+j+1];
55                 }else{
56                     sum2+=w[n1+j+1];
57                 }
58             }
59             int cnt1,SUM;
60             cnt1=n2-cnt,SUM=sum1-sum2;
61             vector<int>::iterator it;
62             it=lower_bound(V[cnt1].begin(),V[cnt1].end(),-SUM);
63             if(it!=V[cnt1].end()&&abs(*it+SUM)<ans){
64                 ans=abs(*it+SUM);
65             }
66             if(it!=V[cnt1].begin()){
67                 it--;
68                 if(abs(*it+SUM)<ans)
69                     ans=abs(*it+SUM);
70             }
71         }
72         printf("%d
",ans);
73     }
74 return 0;
75 }
View Code
原文地址:https://www.cnblogs.com/LQLlulu/p/9017399.html