题目大意:给定一个含有n个元素的集合,求出其中一个子集,使得在子集内所有元素的和的绝对值最小的情况下,子集元素个数最小。输出最小值和最少元素个数。n <= 35.
这个题是很显然的折半搜索,我们先搜前一半,再搜后一半,搜到后一半的时候,我们在前一半元素之中二分去找最接近0的那一个值来更新答案。
思路很清晰但是坑特别多……
首先是,如果你采用我这种算法,你需要特判掉输入中有0的情况。否则的话会出现一些奇怪的问题……
还有就是需要判重。因为我们是二分一个值之后还要找其前驱后继。后继没问题,但是如果有多个前驱的值是一样的而选取的元素不一样,那么是不行的,因为我们会取到元素最多的那一个。方法就是在sort之后判重,只取值相同的那些中元素最少的一个。
还有就是,我们在搜前一半的时候需要更新答案。(这个忘了让我调了好久……)
本题拍了几千组数据也没问题……但是想不到在这翻车。
mrclr的代码有误都能过orzzzzzz
之后看一下代码吧……与二进制相比唯一优秀的可能是比较清真。
#include<cstdio> #include<algorithm> #include<cstring> #include<iostream> #include<cmath> #include<set> #include<queue> #define rep(i,a,n) for(int i = a;i <= n;i++) #define per(i,n,a) for(int i = n;i >= a;i--) #define enter putchar(' ') #define fr friend inline #define y1 poj using namespace std; typedef long long ll; const int M = 1000005; const ll INF = 1e18; const double eps = 1e-7; #define pr pair<ll,int> #define mp make_pair #define fi first #define sc second ll read() { ll ans = 0,op = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') op = -1; ch = getchar(); } while(ch >= '0' && ch <= '9') { ans *= 10; ans += ch - '0'; ch = getchar(); } return ans * op; } ll n,a[105],cnt,ans,minn,k,tot; pr num[M],res[M]; ll Abs(ll x) { return x > 0 ? x : -x; } bool cmp(pr p, pr q) { if(p.fi == q.fi) return p.sc < q.sc; return p.fi < q.fi; } void dfs(int step,ll sum,ll take) { if(step > k) { if(take != 0) { ll f = Abs(sum); if(f < minn || (f == minn && take < ans)) minn = f,ans = take; } num[++cnt].fi = sum,num[cnt].sc = take; return; } dfs(step+1,sum,take),dfs(step+1,sum+a[step],take+1); } void judge(int p,ll c,ll t) { if(p > tot || p < 1) return; ll cur = Abs(res[p].fi + c),now = res[p].sc + t; if(!now) return; if(cur < minn || (cur == minn && now < ans)) minn = cur,ans = now; } void dfs2(int step,ll sum,ll take) { if(step == k) { int g = lower_bound(res+1,res+1+tot,mp(-sum,0),cmp) - res; judge(g,sum,take),judge(g+1,sum,take),judge(g-1,sum,take); return; } dfs2(step-1,sum,take),dfs2(step-1,sum+a[step],take+1); } int main() { while(1) { n = read(),k = n >> 1;if(!n) break; memset(num,0,sizeof(num)); memset(res,0,sizeof(res)); memset(a,0,sizeof(a)); bool flag = 0; rep(i,1,n) { a[i] = read(); if(a[i] == 0) flag = 1; } if(flag) {printf("0 1 ");continue;} minn = INF,ans = a[0],cnt = tot = 0; dfs(1,0,0),sort(num+1,num+1+cnt,cmp); res[++tot] = num[1]; rep(i,2,cnt) if(num[i].fi != num[i-1].fi) res[++tot] = num[i]; dfs2(n,0,0); printf("%lld %lld ",minn,ans); } return 0; }