题意:
给一串数字,选取其中的数字,使其值最小,并且选取数字的个数尽可能少.
分析:
因为数字范围很大,因此不能使用动态规划来计算,从而考虑折半枚举来进行计算。
将这串数字划分为两部分,第一部分用字典序枚举的方式输出所有的可能结果,将结果输入数组,再对数组进行升序。
再对该串数字的另一部分,进行字典序枚举,对于每一种可能性,在第一个数组中对这种可能性的负数进行二分查找,并与ans进行比较。
【本题在各种区间细节中有些麻烦,需要注意,详情见代码】
代码:
#include <cstdio>
#include <iostream>
#include <queue>
#include <algorithm>
#define rep(i,a,b) for(int i = a;i <= b;i++)
using namespace std;
typedef long long ll;
const ll SIZE = 1<<18;
int n,ans_num;
ll a[40];
ll ans;
struct TMP1{
ll v;
int id;
}tmp1[SIZE];
bool operator < (TMP1 x,TMP1 y){
if(x.v!=y.v)
return x.v<y.v;
else
return x.id<y.id;
}
ll abss(ll x){
if(x < 0) x = (-1)*x;
return x;
}
int main()
{
while(~scanf("%d",&n),n)
{
rep(i,1,n){
scanf("%lld",&a[i]);
}
ans_num = 0;
ans = 1e17;
int k = 0;
//用字典序枚举出1-n/2物品的所有取用方式
rep(i,1,(1<<(n/2))-1){
ll tmpv = 0;
int tmpn = 0;
rep(j,0,n/2-1){
int x = (i>>j)&1;
if(x){
tmpv += a[n/2 - j];
tmpn++;
}
}
++k;
tmp1[k].v = tmpv;
tmp1[k].id = tmpn;
if(ans > abss(tmpv)){
ans = abss(tmpv);
ans_num = tmpn;
}
else if(ans == abss(tmpv) && ans_num > tmpn) ans_num = tmpn;
}
//删去无用元素
sort(tmp1+1,tmp1+1+k);
int m1 = 1;
rep(i,2,k){
if(tmp1[m1].v != tmp1[i].v){
++m1;
tmp1[m1].v = tmp1[i].v;
tmp1[m1].id = tmp1[i].id;
}
}
//枚举另外一边的元素
rep(i,1,(1<<(n-n/2))-1){
ll tmpv = 0;
int tmpn = 0;
rep(j,0,n-n/2-1){
int x = (i>>j)&1;
if(x){
tmpv += a[j+n/2+1];
tmpn++;
}
}
TMP1 tp;
tp.v = (-1)*tmpv;
tp.id = tmpn;
if(ans > abss(tmpv)){
ans = abss(tmpv);
ans_num = tmpn;
}
else if(ans == abss(tmpv) && ans_num > tmpn) ans_num = tmpn;
int kk1 = lower_bound(tmp1+1,tmp1+m1+1,tp)-tmp1;
if(kk1 <= 0) kk1 = 1;
if(kk1 > m1) kk1 = m1;
if(abss(tmpv+tmp1[kk1].v)<ans)
{
ans = abss(tmpv+tmp1[kk1].v);
ans_num = tmpn+tmp1[kk1].id;
}
else if(abss(tmpv+tmp1[kk1].v) == ans && (tmpn+tmp1[kk1].id) < ans_num)
ans_num = tmpn+tmp1[kk1].id;
//有一点是debug长达3h的关键错误
//lower_bound是返回 >= 的值
//upper_bound是返回 > 的值
//千万不要以为lower_bound是返回 <= !!!!!!!!!
int kk2;
if(kk1 > 1) kk2 = kk1-1;
else continue;
if(abss(tmpv+tmp1[kk2].v)<ans)
{
ans = abss(tmpv+tmp1[kk2].v);
ans_num = tmpn+tmp1[kk2].id;
}
else if(abss(tmpv+tmp1[kk2].v) == ans && (tmpn+tmp1[kk2].id) < ans_num)
ans_num = tmpn+tmp1[kk2].id;
}
printf("%lld %d\n",ans,ans_num);
}
return 0;
}