• Luogu P3092 [USACO13NOV]没有找零No Change【状压/二分】By cellur925


    题目传送门

    可能是我退役/NOIP前做的最后一道状压...

    题目大意:给你(k)个硬币,FJ想按顺序买(n)个物品,但是不能找零,问你最后最多剩下多少钱。

    注意到(k<=16),提示状压。开始设计的状态是(f[i])表示在状态(i)下最多剩的钱数,后来发现不好搞因为可能有无解的情况。这种情况我们不妨把状态设计的再“退化”一点,也就是不那么贴近最终的结果,只是一个中间的部分。(f[i])为在状态(i)下,最多能购买的物品数。

    因为是按顺序购买的,所以有一定的单调性(单调递增)。我们在转移的时候考虑从哪些状态转移到当前状态,还是异或得到子集的思路来转移,但是我们更新的是买的物品数,每次暴力查的话可能会是复杂度再累乘一个(O(n))然后爆炸。刚才我们说到单调性,可以利用这个性质帮助求解,用一个(upperbound)就能把复杂度降到(O(2^k*k*log))

    设用二分找到的这个位置为(pos),那么我们的转移方程就是(f[i]=max(f[i],pos-1))。每次当我们的状态能买(n)个时,就更新下答案。最后判无解的时候看需求是否大于供给。另外下标最好都从0开始搞...。(WA了一次)

    总之一道二分+状压好题~(逃)

    #include<cstdio>
    #include<algorithm>
    
    using namespace std;
    typedef long long ll;
    
    int k,n,fake;
    int f[70000];
    ll tot,ans=1e20,sum[100090],val[20];
    
    int main()
    {
    	freopen("1.in","r",stdin);
    	scanf("%d%d",&k,&n);
    	fake=(1<<k)-1;
    	for(int i=0;i<k;i++) scanf("%lld",&val[i]),tot+=val[i];
    	for(int i=1,x=0;i<=n;i++)
    		scanf("%d",&x),sum[i]=sum[i-1]+x;
    	for(int i=0;i<=fake;i++)
    	{
    		for(int j=0;j<k;j++)
    		{
    			if(i&(1<<j))
    			{
    				ll fi=sum[f[i^(1<<j)]]+val[j];
    				int pos=upper_bound(sum+1,sum+1+n,fi)-sum;
    				f[i]=max(f[i],pos-1);
    			}
    		}
    		if(f[i]==n)
    		{
    			ll tmp=0;
    			for(int j=0;j<k;j++)
    				if(i&(1<<j)) tmp+=val[j]; 
    			ans=min(ans,tmp);
    		}
    	}
    	if(ans>tot) printf("-1
    ");	
    	else printf("%lld
    ",tot-ans);
    	return 0;
    }
    
  • 相关阅读:
    liunx上升级python2至python3
    python之logging日志
    c# 脚本引擎 脚本编辑器
    设置 BCompare 打开文件时的默认字符编码
    Python调用动态库,获取BSTR字符串
    服务器CPU100%的排查日志
    栈(Stack)
    搭建个人的github.io博客
    django.core.exceptions.ImproperlyConfigured: SQLite 3.9.0 or later is required (found 3.7.17)
    virtualenv中使用python的虚拟环境
  • 原文地址:https://www.cnblogs.com/nopartyfoucaodong/p/9907166.html
Copyright © 2020-2023  润新知