链接:
http://poj.org/problem?id=3977
题意:
给你n个数,n最大35,让你从中选几个数,不能选0个,使它们和的绝对值最小,如果有一样的,取个数最小的
思路:
子集个数共有2^n个,所以不能全部枚举,但是可以分为两部分枚举;枚举一半的所有情况,然后后一半二分即可;
代码:
1 #include"bits/stdc++.h" 2 #define N 45 3 using namespace std; 4 typedef long long LL; 5 6 int n; 7 LL a[N]; 8 9 LL Abs(LL x) 10 { 11 return x<0?-x:x; 12 } 13 14 int main() 15 { 16 while(scanf("%d", &n), n) 17 { 18 for(int i=0; i<n; i++) 19 scanf("%I64d", &a[i]); 20 21 map<LL, int> M; 22 map<LL, int>::iterator it; 23 pair<LL, int> ans(Abs(a[0]), 1); 24 25 for(int i=1; i<1<<(n/2); i++) 26 { 27 LL sum = 0;int cnt = 0; 28 for(int j=0; j<(n/2); j++) 29 { 30 if((i>>j)&1) 31 { 32 sum += a[j]; 33 cnt ++; 34 } 35 } 36 ans = min(ans, make_pair(Abs(sum), cnt));///全部是前半部分的; 37 if(M[sum])///更新cnt为小的; 38 M[sum] = min(M[sum], cnt); 39 else 40 M[sum] = cnt; 41 } 42 43 for(int i=1; i<1<<(n-n/2); i++) 44 { 45 LL sum = 0;int cnt = 0; 46 for(int j=0; j<(n-n/2); j++) 47 { 48 if((i>>j)&1) 49 { 50 sum += a[j+n/2]; 51 cnt ++; 52 } 53 } 54 ans = min(ans, make_pair(Abs(sum), cnt));///全部是后半部分的; 55 56 it = M.lower_bound(-sum);///找到第一个大于-sum的位置,然后取两种情况的最小值; 57 58 if(it != M.end()) 59 ans = min(ans, make_pair(Abs(sum+it->first), cnt+it->second)); 60 if(it != M.begin()) 61 { 62 it--; 63 ans = min(ans, make_pair(Abs(sum+it->first), cnt+it->second)); 64 } 65 } 66 printf("%I64d %d ", ans.first, ans.second); 67 } 68 return 0; 69 }