题意:
给n(n<=35)个数,求一个它的子集,使其中元素和的绝对值最小。
分析:
折半枚举。
代码:
//poj 3977
//sep9
#include <iostream>
#include <map>
using namespace std;
typedef long long LL;
const int maxN=40;
LL a[maxN];
LL myabs(LL x)
{return x>=0?x:-x;
}
int main()
{int n;while(cin>>n&&n){for(int i=0;i<n;++i)cin>>a[i];map<LL,int> m; pair<LL,int> res(myabs(a[0]),1);for(int i=0;i<1<<(n/2);++i){LL sum=0;int num=0;for(int j=0;j<n/2;++j)if((i>>j)&1){sum+=a[j];++num;}if(!num) continue;res=min(res,make_pair(myabs(sum),num)); map<LL,int>::iterator iter=m.find(sum);if(iter!=m.end())iter->second=min(iter->second,num);else m[sum]=num;}for(int i=0;i<1<<(n-n/2);++i){LL sum=0;int num=0;for(int j=0;j<(n-n/2);++j)if((i>>j)&1){sum+=a[n/2+j];++num;}if(!num) continue;res=min(res,make_pair(myabs(sum),num));map<LL,int>::iterator iter=m.lower_bound(-sum);if(iter!=m.end())res=min(res,make_pair(myabs(sum+iter->first),num+iter->second));if(iter!=m.begin()){--iter;res=min(res,make_pair(myabs(sum+iter->first),num+iter->second));}}cout<<res.first<<" "<<res.second<<endl;}return 0;
}