[HDU 3017] Treasure Division (暴力+折半搜索)

HDU - 3017
一个裸的背包问题,有 N个物体,每个有个体积和价值
问分成两堆,每堆数量相差不超过 1,使得价值差最小

这题数据的特点是体积特别大,有 230 ,意味着不能使用背包DP来做
但是N又特别小,所以其实这题正解是暴力枚举
但是显然,暴力枚举的复杂度是 230 次方 (一个物体选或不选)

所以我们可以用上一个叫做折半搜索的小技巧
把N分成相等的两堆,每堆进行枚举,复杂度为 215+215
然后枚举第一堆选 i的情况,在另一堆找到 N/2i 个数的,去更新答案
但是这样依旧会超时,所以我们加上一个剪枝
通过变换更新答案的不等式,再对第二堆的情况排序
二分查找其中能更新答案的情况的上界和下界,再进行更新

#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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值