qwb与李主席 状压+二分

18 篇文章 0 订阅
3 篇文章 0 订阅

Description

qwb和李主席打算平分一堆宝藏,他们想确保分配公平,可惜他们都太懒了,你能帮助他们嘛?

Input

输入包含多组测试数据,处理到文件结束。

每组测试数据的第一行是一个正整数N(0 <= N <=36 )表示物品的总个数.。

接下来输入N个浮点数(最多精确到分),表示每个物品的价值V(0 < V <= 1e9)。

Output

对于每组测试数据,输出能够使qwb和李主席各自所得到的物品的总价值之差的最小值(精确到分),每组测试数据输出占一行。

Sample Input
3 0.01 0.1 1
2 1 1

Sample Output
0.89
0.00

HINT

首先年轻的我会想到dp,可能因为n很小 v很大,所以 背包是不可行的,只能用二分查找后者dfs。
对,因为是只有两个人,所以你会想到枚举一个,再二分sum/2-前面的是完全可行的。

因为数据范围是 36 单纯的状压肯定会超时。
然后我们可以想到一个人拿东西可能全在左边拿,或者全在右边拿。或者既在左边又在中间拿,对,说了和没说是一样的。但是这个揭露一个关键点,可以把这个东西枚举一边,二分另一边。所以就简单了,而且一边进行18个进行状压枚举,完全可以把所有的状态都弄出来。然后枚举一边,再二分另一边得到最正确的结果。
第一个坑点,因为是浮点数,要求精度比较高,精确到两位后会到11位,可以用long double,也可以化为 longlong 化成整型,因为后面要进行/2,所以200就很完美了。另一个点是 求两边相差最小,abs(sum-2(一个人的物品价值就好))

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

ll a[1<<18],b[1<<18],c[1<<18],d[1<<18];

void gao(ll g[],ll h[],int n)
{
    ll gg=1<<n;
    for(int i=0;i<gg;i++)
    {
        for(int j=0;j<n;j++)
        {
            if(i&(1<<j)) 
                h[i]+=g[j];
        }
    }
}



int main()
{
    int n;
    while(scanf("%d",&n)!=EOF)
    {
    ll cnta=0,cntb=0;
    ll sum=0;

    for(int i=0;i<n;i++)
    {
        double x;
        scanf("%lf",&x);
        ll y=round(x*200);
        sum+=y;
        if(i<=n/2) c[cnta++]=y;
        else d[cntb++]=y;
    }
    gao(c,a,cnta);
    gao(d,b,cntb);
    cnta=(1<<cnta);
    cntb=(1<<cntb);
    sort(a,a+cnta);
    ll minn=1e18;
    for(int i=0;i<cntb;i++)
    {
        int pos=lower_bound(a,a+cnta,sum/2-b[i])-a;
        minn=min(minn,abs(sum-2*(b[i]+a[pos])));
        if(pos>=1) minn=min(minn,abs(sum-2*(b[i]+a[pos-1])));
    }
    printf("%.2f\n",minn*1.0/200 );
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值