Subset

题目描述:

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.

输入:

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

输出:

For each data set in the input print two integers, the minimum absolute sum and the number of elements in the optimal subset.

样例输入:

1
10
3
20 100 -100
0

样例输出:

10 1
0 2

题目大意:

让你从n个数里面找任意个数(>0),使他们的和的绝对值最小,如果有多组和一样最小,输出最小和的绝对值且最小个数。

2^35,会超时,所以考虑折半枚举,减少时间复杂度,并用二分,查找绝对值最小的和

代码如下:

#include<stdio.h>
#include<algorithm>
#include<iostream>
const int maxn=1000005;
long long a[maxn];
int qq;
using namespace std;
struct node{
	long long value;
	int len;
}nod[maxn],cun[maxn];
bool cmp(node a,node b)
{
	if(a.value==b.value)return a.len<b.len;
	else return a.value<b.value;
}
int findd(long long what)
{
	int lb=-1,ub=qq,mid;
	while(ub-lb>1)
	{
		mid=(lb+ub)/2;
		if(nod[mid].value>=what)ub=mid;
		else lb=mid;
	}
	return ub;
}
int main()
{
	int n;
	while(1)
	{
		scanf("%d",&n);
		long long ans=1e15+5,res=99999;//一开始写的是ans=9999999,然后一直wrong 
		if(n==0)
		{
			break;
		}
		int i,j,k;
		for(i=0;i<n;i++)
		{
			scanf("%lld",&a[i]);
		}
		int m=n/2;
		int s=1<<(n-m);
		for(i=1;i<s;i++)
		{
			int tt=i;
			long long temp=0;
			int cnt=0;
			for(j=m;j<n;j++)
			{
				if(tt&1)
				{
					temp+=a[j];
					cnt++;
				}
				tt>>=1;
			}
			cun[i].value=temp;
			cun[i].len=cnt;
		}
		sort(cun+1,cun+s,cmp);//后半段可能有的值 
		qq=2;
		nod[0].value=0;
		nod[0].len=0;//一定要有 
		nod[1].value=cun[1].value;
		nod[1].len=cun[1].len;
		for(i=2;i<s;i++)
		{
			if(cun[i].value!=nod[qq-1].value)
			{
				nod[qq].value=cun[i].value;
				nod[qq++].len=cun[i].len;
			}
		}
		sort(nod,nod+qq,cmp);
		//减少重复 
		s=1<<m;
		for(i=0;i<s;i++)
		{
			long long temp=0;
			int cnt=0,tt=i;
			for(j=0;j<m;j++)
			{
				if(tt&1)
				{
					temp+=a[j];
					cnt++;
				}
				tt>>=1;
			}
			long long temp1=-temp;
			int pos=findd(temp1);
			int small=max(0,pos-1);
			int big=min(pos+1,qq-1);
			for(j=small;j<=big;j++)
			{
				long long en=nod[j].value+temp;
				int tm=nod[j].len+cnt;
				if(en<0)en=-en;
				if(tm==0&&en==0)
					continue;
				if(en<ans||(en==ans&&tm<res))
				{
					ans=en;
					res=tm;
				 }
			}
		}//前半段和后半段的叠加 
		printf("%lld %lld\n",ans,res);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值