題意ios
在雙胞胎兄弟Eric與R.W的生日會上,他們共收到了N個禮物,生日事後他們決定分配這N個禮物(numv+numw=N)。對於每一個禮物他們倆有着各自心中的價值vi和wi,他們要求各自分到的禮物數目|numv-numw|<=1,而且各自所衡量的禮物價值的差值|sumv-sumw|儘量小,如今他們想知道最小的差值是多少。ide
分析spa
這是中途相遇法的模板題code
每一個禮物要麼屬於Eric,要麼屬於R.W,因此若是暴力的話是2^30,顯然會超。blog
使用中途相遇法能夠將複雜度降到2^15左右,非常神奇。string
1. 先將N個禮物分紅兩份,第一份有n/2個禮物,第二份有n-n/2個禮物。it
2. 而後枚舉第一份中有哪些屬於Eric,哪些屬於R.W。cnt來記錄第一份中Eric的禮物數目,sum1是第一份中Eric的禮物價值和,sum2是R.W的禮物價值和。而後用一個vector,把每一個sum1-sum2都加到下標爲cnt的vector中。io
3. 用相似的方法,枚舉第二份中哪些屬於Eric,哪些屬於R.W。sum1,sum2,cnt的含義相同。而後在下標爲n-n/2-cnt的vector中找和這個sum1-sum2相加最小的值,而後判斷是否要更新ans。模板
就是這樣,用這個題來學中途相遇法了。。。步驟3中的小細節仍是比較神奇的。class
下面是代碼
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\n",ans); 73 } 74 return 0; 75 }