HDU - 3017
一个裸的背包问题,有 N个物体,每个有个体积和价值
问分成两堆,每堆数量相差不超过 1,使得价值差最小
这题数据的特点是体积特别大,有
230
,意味着不能使用背包DP来做
但是N又特别小,所以其实这题正解是暴力枚举
但是显然,暴力枚举的复杂度是
230
次方 (一个物体选或不选)
所以我们可以用上一个叫做折半搜索的小技巧
把N分成相等的两堆,每堆进行枚举,复杂度为
215+215
然后枚举第一堆选 i的情况,在另一堆找到
N/2−i
个数的,去更新答案
但是这样依旧会超时,所以我们加上一个剪枝
通过变换更新答案的不等式,再对第二堆的情况排序
二分查找其中能更新答案的情况的上界和下界,再进行更新
#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <map>
#include <set>
#include <queue>
using namespace std;
typedef pair<int,int> Pii;
typedef long long LL;
typedef unsigned long long ULL;
typedef double DBL;
typedef long double LDBL;
#define MST(a,b) memset(a,b,sizeof(a))
#define CLR(a) MST(a,0)
#define Pow2(a) (a*a)
const LL INF=(LL)1<<53;
int N;
int inpt[50];
LL res0[17][1<<16];
LL res1[17][1<<16];
int siz0[17],siz1[17];
int main()
{
while(~scanf("%d", &N))
{
LL tot=0;
CLR(siz0);CLR(siz1);
for(int i=0; i<N; i++) scanf("%d", &inpt[i]),tot+=inpt[i];
int lt=N/2,rt=N-lt;
for(int mask=0; mask<1<<lt; mask++)
{
int tcnt=__builtin_popcount(mask);
LL sum=0;
for(int i=0; i<lt; i++)
{
if(mask&(1<<i)) sum+=inpt[i];
}
res0[tcnt][siz0[tcnt]++]=sum;
}
for(int mask=0; mask<1<<rt; mask++)
{
int tcnt=__builtin_popcount(mask);
LL sum=0;
for(int i=0; i<rt; i++)
{
if(mask&(1<<i)) sum+=inpt[lt+i];
}
res1[tcnt][siz1[tcnt]++]=sum;
}
LL ans=INF;
for(int i=0; i<=lt; i++)
{
sort(res1[lt-i],res1[lt-i]+siz1[lt-i]);
for(int j=0; j<siz0[i]; j++)
{
LL *lb=lower_bound(res1[lt-i], res1[lt-i]+siz1[lt-i], (tot-ans)/2-res0[i][j]-1);
LL *ub=upper_bound(res1[lt-i], res1[lt-i]+siz1[lt-i], (tot+ans)/2-res0[i][j]+1);
for(LL *k=lb; k<ub; k++) ans=min(ans, abs(2*(res0[i][j]+*k)-tot));
}
}
cout << ans << '\n';
}
return 0;
}