• 【NOIP模拟】购物


    题面

    双11就要来啦!Yuno 刚刚获得了X 元的奖金。那么是不是应该清空下购物车呢?
    购物车总共有 N 个物品,每个物品的价格为 Vi ,Yuno 想尽可能地把奖金给花光,所以她要精心选择一些商品,使得其价格总和最接近但又不会超过奖金的金额。那么 Yuno 最后最少可以剩下多少钱呢?

    10% 的数据:N ≤ 10
    40% 的数据:N ≤ 20, X,Vi ≤ 10000
    100% 的数据:N ≤ 40, X,Vi ≤ 109

    分析

    40%的数据可以01背包做,而220=10242说明也可以暴搜,即枚举子集来做。

    其实可以发现40=20+20,这说明什么?分成两半来搜就可以了。于是我们想到了用折半搜索,搜索树的深度减小一半,也就只有220的级别

    关键是需要meet in mid,怎么meet呢?对于第二次搜索的每一个得到的子集,我们在第一个搜索得到的子集中找一个对应的,使这两个加起来小于X并且最大。

    而第一个搜索得到的子集排序,二分就可以查到了。于是时间复杂度是220*log220  也就是20*220,还是可以接受

    代码

    #include<bits/stdc++.h>
    using namespace std;
    #define N 11000000
    #define ll long long
    ll w[N],tmp[N];
    ll n,x,pos,cnt,ans,mid;
    template<class T>
    inline void read(T &x)
    {
        x=0;ll f=1;static char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        x*=f;
    }
    
    inline void dfs1(ll k,ll now)
    {
        if(now>x)return;
        ans=min(ans,x-now);
        if(k>mid)
        {
            tmp[++cnt]=now;
            return ;
        }
        dfs1(k+1,now);dfs1(k+1,now+w[k]);
    }
    
    inline void dfs2(ll k,ll now)
    {
        if(now>x)return;
        if(k>n)
        {
            pos=upper_bound(tmp+1,tmp+1+cnt,x-now)-tmp;
            if(pos)
                ans=min(ans,x-now-tmp[pos-1]);
            return ;
        }
        dfs2(k+1,now);dfs2(k+1,now+w[k]);
    } 
    
    int main()
    {
        read(n),read(x);mid=(n+1)/2;ans=x;
        for(ll i=1;i<=n;i++)read(w[i]);
        dfs1(1,0);
        sort(tmp+1,tmp+1+cnt);
        if(mid+1<=n)dfs2(mid+1,0);
        printf("%lld
    ",ans);            
        return 0;
    }
  • 相关阅读:
    DNS 服务器的配置与管理
    为什么苹果不再需要谷歌地图?
    flash安装时提示无法安装解决方法
    苹果新ipad支持siri吗?答案是不支持!
    HTTP的 Basic 验证
    笑解优酷土豆合并
    crontab简介
    循环链表应用
    计算表达式
    走迷宫 dfs
  • 原文地址:https://www.cnblogs.com/NSD-email0820/p/9925101.html
Copyright © 2020-2023  润新知