折半枚举

Given a list of N integers with absolute values no larger than 10 15, 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 10 15 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

题目分析 : 给你 35 个数字,每个数字只有选或者不选,问你选出数和的绝对值最小。
思路分析 :
  分成两组去枚举,对于每一组都可以枚举出所有的情况,对于第二组,这里可以排个序,然后在去重,查的时候二分。然后 map 可以就用来优化二分。      
  
代码示例 :
const int eps = 1e6+5;
const double pi = acos(-1.0);
const int inf = 1<<29;
#define Max(a,b) a>b?a:b
#define Min(a,b) a>b?b:a
#define ll long long

ll pre[50];
struct node
{
    ll num;
    int cnt;
}a[eps];

map<ll, int>mp;

ll fb(ll x){
    return x<0?-x:x;
}

int main() {
    //freopen("in.txt", "r", stdin);
    //freopen("out.txt", "w", stdout);
    int n;
    
    while(~scanf("%d", &n) && n){
        mp.clear(); 
        for(int i = 1; i <= n; i++){
            scanf("%lld", &pre[i]);
        } 
        int n1 = n / 2;
        int n2 = n - n1;
        
        ll ans = 99999999999999999;
        int len = 99999;
        for(int i = 0; i < (1<<n1); i++){
            int p = i;
            ll sum = 0;
            int cnt = 0;
            for(int j = 1; j <= n1; j++){
                if (p & 1) {
                    sum += pre[j];
                    cnt++;
                }
                p >>= 1;
            }
            a[i].num = sum;
            a[i].cnt = cnt;
            
            if (i == 0) continue;
            ll f = fb(a[i].num);
            if (f < ans) {ans = f; len = a[i].cnt;}
            else if (f == ans) len = min(len, a[i].cnt); 
        }

        for(int i = 1; i < (1<<n2); i++){
            int p = i;
            ll sum = 0;
            int cnt = 0;
            for(int j = 1+n1; j <= n1+n2; j++){
                if (p & 1) {
                    sum += pre[j];
                    cnt++;
                }
                p >>= 1;
            }
            if (mp.count(sum)) mp[sum] = min(mp[sum], cnt);
            else mp[sum] = cnt;
        }

        map<ll, int>::iterator it;
        it = mp.begin();
        ll f = fb(it->first);
        if (f < ans) {ans = f; len = it->second;}
        else if (f == ans) len = min(len, it->second);
        for(int i = 0; i < (1<<n1); i++){
            if (mp.count(-a[i].num)){
                if (ans == 0) len = min(len, a[i].cnt+mp[-a[i].num]);
                else len = a[i].cnt + mp[-a[i].num];
                ans = 0;
            }
            else {
                it = mp.lower_bound(-a[i].num);
                ll f = it->first + a[i].num;
                f = fb(f);
                if (f < ans) {
                    ans = f;
                    len = it->second + a[i].cnt;
                }
                else if (f == ans) len = min(len, it->second+a[i].cnt);
                
                it--;
                f = it->first + a[i].num;
                f = fb(f);
                if (f < ans) {
                    ans = f;
                    len = it->second + a[i].cnt;
                }
                else if (f == ans) len = min(len, it->second+a[i].cnt);
            } 
        } 
        printf("%lld %d
", ans, len);
    }
    
    return 0;
}


东北日出西边雨 道是无情却有情
原文地址:https://www.cnblogs.com/ccut-ry/p/8367146.html