• P3092 [USACO13NOV]No Change G 状压 + 二分


    P3092 [USACO13NOV]No Change G 状压 + 二分

    题意

    约翰到商场购物,他的钱包里有(K)((1 <= K <= 16))个硬币,面值的范围是([1,1e9])

    约翰想按顺序买 (N)个物品((1 <= N <= 100,000)),第i个物品需要花费(c(i))块钱,((1 <= c(i) <= 10,000))。

    在依次进行的购买(N)个物品的过程中,约翰可以随时停下来付款,每次付款只用一个硬币,支付购买的内容是从上一次支付后开始到现在的这些所有物品(前提是该硬币足以支付这些物品的费用)。不幸的是,商场的收银机坏了,如果约翰支付的硬币面值大于所需的费用,他不会得到任何找零。

    请计算出在购买完(N)个物品后,约翰最多剩下多少钱。如果无法完成购买,输出(-1)

    分析

    • 注意审题,每次只能投一枚硬币
    • (k)比较小,考虑状压DP,(dp[i])表示状态(i)下的最多能买到第几个物品
    • 显然第(k) 枚硬币是从上一个硬币转移而来的,转移方程(dp[i] = max lbrace dp[i异或k] + f(k) brace)
    • (f(k))可以二分,于是总体复杂度(O(2^kklogn))
    • 由于要知道多少钱,再开一个辅助数组即可

    代码

    int n,k;
    
    int dp[1 << 16];
    ll f[1 << 16];
    ll sum[100005];
    int a[100005];
    
    int check(int x,int t){
    	int l = t;
    	int r = n;
    	while(l < r){
    		int mid = l + r + 1 >> 1;
    		if(sum[mid] - sum[t - 1] <= x) l = mid;
    		else r = mid - 1;
    	}
    	return l;
    }
    
    int main(){
    	ll res = 1e18;
    	ll tot = 0;
    	k = readint();
    	n = readint();
    	for(int i = 0;i < k;i++)
    		a[i] = readint(),tot += a[i];
    	for(int i = 1;i <= n;i++)
    		sum[i] = sum[i - 1] + readll();
    	for(int i = 1;i < (1 << k);i++)
    		for(int j = 0;j < k;j++)
    			if(i & (1 << j)) {
    				int x = i ^ (1 << j);
    				ll tmp = check(a[j],dp[x] + 1);
    				if(tmp > dp[i]) {
    					dp[i] = tmp;
    					f[i] = f[x] + a[j];
    				}
    				if(tmp == n) 
    					res = min(res,f[i]);
    			}
    	if(tot - res < 0) puts("-1");
    	else cout << tot - res;
    }
    
  • 相关阅读:
    从csdn转移到博客园的一篇测试文章
    接口与抽象类的区别
    python网络爬虫进阶之HTTP原理,爬虫的基本原理,Cookies和代理介绍
    python验证码识别(2)极验滑动验证码识别
    VMWare虚拟机应用介绍
    Rpg maker mv角色扮演游戏制作大师简介
    python数据挖掘之数据探索第一篇
    python数据分析&挖掘,机器学习环境配置
    python爬取豆瓣视频信息代码
    python验证码处理(1)
  • 原文地址:https://www.cnblogs.com/hznumqf/p/14036466.html
Copyright © 2020-2023  润新知