POJ 3977Subset

来源:互联网 发布:人工智能图像理解 编辑:程序博客网 时间:2024/06/07 10:51

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.

题意是给定n个数字,要求找到其一个非空子集,使得这个子集内绝对值的和最小。

类似的题目,由于很多数根本不会出现,而且数字非常大,一般来说都是折半搜索,先扫一边,在另一边用stl里的map查询有无,但是或许是因为自己写挫了的缘故,超时了。

所以最后我选择了用数组模拟,由于还有统计最少需要多少个,故开了一个结构体,v表示它的和,cnt表示构成它所需的次数,使用折半搜索的思想,折半先构造出前一半所能达到的和。接着构造后一半能达到的和sum,查找-sum是否能在前一半种找到,找不到就找最贴近的那一个。这时问题转换成了如何查找所有数内,与某个数最贴近的数,幸好c++算法库里自带lower_bound函数,算以我们可以在算出前一半时排序,在算后一半时边算达到的和sum,边查找-sum,由于最贴近的那一个如果-sum不存在,那就是大于-sum最小的值了,而需要的是绝对值,所以我们还要将这是lower_bound算出的pos-1,来求出小于-sum最大的值。注意lower_bound如果查不到返回的是右端点的值,所以如果这时pos如果是右端点,就给它-1,还要保证在求小于-sum最大的值时,pos-1是>0的。

下附AC代码

#include<iostream>#include<algorithm>#define maxn 40using namespace std;typedef long long ll ;int n;ll myabs(ll i){return i>=0 ? i : -i;}struct nod{ll v;int cnt;nod(ll a,int b){v=a;cnt=b;}nod(){}}a[1<<20];ll num[maxn];bool operator <(nod i,nod j){if(i.v!=j.v)return i.v<j.v;return i.cnt<j.cnt;}int main(){while(cin>>n && n){ll cnt1=0,cnt2=0;for(int i=0;i<n;i++)cin>>num[i];nod ans=nod(myabs(num[0]),1);int mid=n>>1;for(ll i=0;i<(1<<mid);i++){int ncnt=0;a[i].v=0;for(int j=0;j<mid;j++)if((i>>j) & 1){a[i].v+=num[j];ncnt++;}a[i].cnt=ncnt;if(i)ans=min(ans,nod(myabs(a[i].v),a[i].cnt));}sort(a,a+(1<<mid));for(ll i=0;i<(1<<mid)-1;i++)if(a[i].v==a[i+1].v){a[i+1].cnt=a[i].cnt;} for(ll i=1;i<(1<<(n-mid));i++){ll nsum=0;int ncnt=0;for(int j=0;j<n-mid;j++)if((i>>j) & 1){nsum+=num[mid+j];ncnt++;}nod now=nod(myabs(nsum),ncnt);ans=min(ans,now);ll pos=lower_bound(a,a+(1<<mid),nod(-nsum,0))-a;for(int j=min(pos,(long long)((1<<mid)-1));j>=max(pos-1,0ll);j--){ans=min(ans,nod(myabs(a[j].v+nsum),a[j].cnt+ncnt)); }}cout<<ans.v<<' '<<ans.cnt<<endl;}}


原创粉丝点击