Subset
| Time Limit: 30000MS | Memory Limit: 65536K | |
| Total Submissions: 4863 | Accepted: 892 |
Description
Given a list of N integers with absolute values no larger than 1015, find a non empty subset of these numbers which minimizes the absolute value of the sum of its elements. In case there are multiple subsets, choose the one with fewer elements.
Input
The input contains multiple data sets, the first line of each data set contains N <= 35, the number of elements, the next line contains N numbers no larger than 1015 in absolute value and separated by a single space. The input is terminated with N = 0
Output
For each data set in the input print two integers, the minimum absolute sum and the number of elements in the optimal subset.
Sample Input
1
10
3
20 100 -100
0
Sample Output
10 1
0 2
Source
题目大意就是给你一个数,你要在其中跳出一些数,使他们的和的绝对值越小越好,在前者一样的情况下,要使选出数的个数也越小越好.
由于n只有35,如果用O(2^35)的算法是过不了的.显然,这题很符合折半搜索.我们可以压位将前一半的所有组合记录下来,用了2^(n/2)的时间.
然后,我们再将另一半排序.我们枚举每一个前一半的状态(注意是状态,也就是多个数的组合(当然也可以有1个数)),然后在后一半二分查找,使当前状态的sum与查找到的状态(注意也是状态)的和越接近0越好.
这一步效率是O(2^18*log2(2^18))的,也就是O(2^18*18)的,稳过.
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #define LL long long 5 using namespace std; 6 int n,m,s1,s2; 7 LL a[40],anssum,ansnum; 8 struct data{ 9 LL v,c; 10 bool operator < (const data &u) const { 11 return v<u.v||v==u.v&&c>u.c; 12 } 13 }L[1<<20],R[1<<20]; 14 LL abso(LL x){return x>0?x:-x;} 15 int bin_find_sml(int i){ 16 int l=1,r=s1-1,mid,ret=1; 17 while (l<=r){ 18 mid=(l+r)>>1; 19 if (L[mid].v==-R[i].v) return mid; else 20 if (L[mid].v<-R[i].v) ret=mid,l=mid+1; else r=mid-1; 21 } 22 return ret; 23 } 24 int main(){ 25 while (scanf("%d",&n)&&n){ 26 memset(a,0,sizeof a); 27 for (int i=0; i<n; i++) scanf("%lld",&a[i]); sort(a,a+n); 28 if (n==1){printf("%lld 1\n",abso(a[0])); continue;} 29 m=n/2,s1=1<<(m+1),s2=1<<(n-m-1),anssum=ansnum=1e18; 30 memset(L,0,sizeof L); 31 for (int i=0; i<s1; i++) 32 for (int j=0; j<=m; j++) if (i&(1<<j)) L[i|(1<<j)].v=L[i].v+a[j],L[i|(1<<j)].c=L[i].c+1; 33 memset(R,0,sizeof R); 34 for (int i=0; i<s2; i++) 35 for (int j=0; j<=n-m-2; j++) if (i&(1<<j)) R[i|(1<<j)].v=R[i].v+a[j+m+1],R[i|(1<<j)].c=R[i].c+1; 36 for (int i=0; i<s1; i++) if (abso(L[i].v)<=anssum&&L[i].c){ 37 if (abso(L[i].v)<anssum) anssum=abso(L[i].v),ansnum=L[i].c; 38 else if (L[i].c<ansnum) ansnum=L[i].c; 39 } 40 for (int i=0; i<s2; i++) if (abso(R[i].v)<=anssum&&R[i].c){ 41 if (abso(R[i].v)<anssum) anssum=abso(R[i].v),ansnum=R[i].c; 42 else if (R[i].c<ansnum) ansnum=R[i].c; 43 } 44 sort(L+1,L+s1); sort(R+1,R+s2); 45 for (int i=1; i<s2; i++){ 46 int x=bin_find_sml(i); 47 while (x<s1){ 48 if (abso(R[i].v+L[x].v)<=anssum){ 49 if (abso(R[i].v+L[x].v)<anssum) anssum=abso(R[i].v+L[x].v),ansnum=R[i].c+L[x].c; 50 else if (R[i].c+L[x].c<ansnum) ansnum=R[i].c+L[x].c; 51 }else{ 52 if (x<s1-1){if (abso(R[i].v+L[x].v)<=abso(R[i].v+L[x+1].v)) break;} 53 else if (x+1==s1) break; 54 } 55 x++; 56 } 57 } 58 printf("%lld %lld\n",anssum,ansnum); 59 } 60 return 0; 61 }
本文介绍了一种使用折半搜索算法解决最小绝对值子集和问题的方法,通过预处理一半数的组合并排序另一半,实现高效查找最接近零的子集和,适用于数列长度不超过35的情况。
887

被折叠的 条评论
为什么被折叠?



