Problem Description
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
0Sample Output
10 1
0 2
题意:多组数据,以 0 为结尾,每组数据给出 n 个数构成的集合,现要在集合中找到一个非空子集,使得这个子集元素和的绝对值尽量小,绝对值相同时,保证元素个数尽量少
思路:
n 最大到 35,每个数有选、不选两种可能,最多有 2^35 个子集
因此暴力枚举的话,一定会 TLE,采用二分的思想,分成两个集合,这样每边最多 18 个元素,分别进行枚举,复杂度降到 2^18
然后枚举其中一个子集,排序后暂存后,再枚举另一个子集,通过二分查找与寻找合适的子集并与第一个集合的子集相加,从而找到绝对值最小的子集
Source Program
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<utility>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<bitset>
#define PI acos(-1.0)
#define INF 0x3f3f3f3f
#define LL long long
#define Pair pair<int,int>
const double EPS = 1E-10;
const int MOD = 1E9+7;
const int N = 1000+5;
const int dx[] = {-1,1,0,0,-1,-1,1,1};
const int dy[] = {0,0,-1,1,-1,1,-1,1};
using namespace std;
LL a[N];
LL Abs(LL x){
return x<0?-x:x;
}
int main(){
int n;
while(scanf("%d",&n)!=EOF&&n) {
for(int i=0;i<n;i++)
scanf("%lld",&a[i]);
int half=n/2;
pair<LL,int> res(Abs(a[0]),1);
map<LL,int> mp;
map<LL,int>::iterator it;
for(int i=1;i<(1<<half);i++) {
LL sum=0;
int cnt=0;
for(int j=0;j<half;j++) {
if(i>>j&1){
sum+=a[j];
cnt++;
}
}
pair<LL,int> temp(Abs(sum),cnt);
res=min(res,temp);
if(mp[sum])
mp[sum]=min(mp[sum],cnt);
else
mp[sum]=cnt;
}
for(int i=1;i<1<<(n-half);i++){
LL sum=0;
int cnt=0;
for(int j=0;j<n-half;j++) {
if(i>>j&1){
sum+=a[j+half];
cnt++;
}
}
pair<LL,int> temp(Abs(sum),cnt);
res=min(res,temp);
it=mp.lower_bound(-sum);
if(it!=mp.end()){
pair<LL,int> temp(Abs(sum+it->first),cnt+it->second);
res=min(res,temp);
}
if(it!=mp.begin()) {
it--;
pair<LL,int> temp(Abs(sum+it->first),cnt+it->second);
res=min(res,temp);
}
}
printf("%lld %d\n",res.first,res.second);
}
return 0;
}