• [BZOJ3312][USACO]不找零(状压DP)


    Description

    约翰带着 N 头奶牛在超市买东西,现在他们正在排队付钱,排在第 i 个位置的奶牛需要支付 Ci元。今天说好所有东西都是约翰请客的,但直到付账的时候,约翰才意识到自己没带钱,身上只有 K张消费卡,第 i 张卡里有 Vi 元余额。

    问题是,这些消费卡都是一次性的,它们可以被收银机读取,但如果卡一旦离开了收银机,卡里的余额就会归零,而且超市也不负责找零!奶牛的队伍很长,不可能再调整她们的位置了,所以一张卡只能支付一段连在一起的账单。而且,一张账单只能用一张消费卡支付,超市的系统不接受用两张或以上的卡支付一笔账单。

    约翰的问题就是按照什么样的顺序来使用这些消费卡,才能让他能为所有的奶牛买单,而且使得剩余的消费卡的余额之和最大呢?

    Input Format

    • 第一行:两个整数 K 和 N ,1 ≤ K ≤ 16, 1 ≤ N ≤ 10^5

    • 第二行到第 K + 1 行:第 i + 1 行有一个整数 Vi,1 ≤ Vi ≤ 10^9

    • 第 K + 2 行到第 K + N + 1 行:第 i + K + 1 行有一个整数 Ci,1 ≤ Ci ≤ 10^4

    Output Format

    单个整数:表示约翰买完所有奶牛的单之后,最多还能剩多少余额,如果他带的卡根本没有办法支付所有的账单,输出 −1。

    Solution

    发现K范围小,考虑状压DP,设(F[S])表示所以消费卡状态为S时可以到达最后面的奶牛编号,即前面的奶牛都付完,

    在二进制下1表示用过了,0表示没有

    那么(F[S|2^{k-1}]=max{Get(card(k),F[S]+1)}) ,

    (Get(s,pos)) 表示可用金额为s, 从(Cow_{pos}) 可到达最远的奶牛坐标,

    那么Get函数可以用前缀和+二分查找优化,否则会超时

    这题挂了好几次,

    1. 写了前缀和,后面忘记写二分查找
    2. 没有用max!WA了,居然还过了好多点

    Code

    #include <cstdio>
    #include <algorithm>
    #define N 100010
    using namespace std;
    
    int n, k, card[19], sum[N], cow[N], f[1 << 19], Ans;
    
    inline int read() {
    	int x = 0, f = 1; 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();}
    	return x * f;
    }
    
    inline int get(int k, int st) {
    	int l = st, r = n, res;
    	while (l <= r) {
    		int mid = (l + r) >> 1;
    		if (sum[mid] - sum[st - 1] <= k)
    			res = mid, l = mid + 1;
    		else r = mid - 1;
    	}
    	return res;
    }
    
    inline int cnt(int S) {
    	int r = 0;
    	for (int i = 1; i <= k; ++i)
    		if (!(S & (1 << (i - 1))))
    			r += card[i];
    	return r;
    }
    
    int main() {
    	k = read(), n = read();
    	for (int i = 1; i <= k; ++i) card[i] = read();
    	for (int i = 1; i <= n; ++i) {
    		cow[i] = read();
    		sum[i] = sum[i - 1] + cow[i];
    	}
    	Ans = -1;
    	for (int S = 0; S < (1 << k); ++S) {
    		if (f[S] == n) continue;
    		for (int i = 1; i <= k; ++i) {
    			int T = (1 << (i - 1));
    			if (S & T) continue;
    			f[S | T] = max(f[S | T], get(card[i], f[S] + 1));
    			if (f[S | T] == n)
    				Ans = max(Ans, cnt(S | T));
    		}
    	}
    	printf("%d
    ", Ans);
    	return 0;
    }
    
  • 相关阅读:
    软件设计工具
    电脑运行 apk
    苹果开发网站
    在Tomcat中部署war
    sql server 2000 语法
    用 xml格式 输出 jsp
    点子网站
    网站推广
    TabWidget
    Java 中文拼音 排序
  • 原文地址:https://www.cnblogs.com/void-f/p/7738549.html
Copyright © 2020-2023  润新知