Codeforces 1154F (DP)

题意:有一个人去买铲子,他需要买正好k把。每把铲子有个标价,并且每把铲子最多只能被买一次。有m种优惠方案,每个优惠方案xi, yi是指如果这次恰好购买了xi把铲子,那么这次购买的铲子中最便宜的yi把将免费。问买k把铲子的最少花费。题目可以分多次购买铲子,只要购买的铲子总数是k把就可以。买过的铲子就不能再买了。

思路:显然,我们需要先对铲子的售价排个序,然后实际上购买的铲子就是最便宜的k把铲子,因为题目中要求购买恰好k把铲子,不能多,所以不存在去购买多余的铲子去凑优惠这种方案。那么现在的问题就变成了如何选择优惠策略,使得总花费最少。设dp[i]为购买前i把铲子的最小花费,初始值为sum[i](sum[i]是排序之后的前缀和),意思是前i个铲子什么优惠都不用。我们枚举使用什么优惠去更新dp[i], dp[i] = min(dp[i - xj] + sum[i] - sum[i - xj +yj])。因为已经对花费排好序了,所以去除最小的yj个的花费是sum[i] - sum[i - xj + yj]。答案是dp[k]。

这个题感觉很水,不知道为什么难度是2300。。。可能是题意不是很好懂把。

代码:

#include <bits/stdc++.h>
#define LL long long
#define pii pair<int, int>
using namespace std;
const int maxn = 200010;
LL a[maxn], sum[maxn], dp[maxn];
pii b[maxn];
int main() {
	int n, m, k;
	scanf("%d%d%d", &n, &m, &k);
	for (int i = 1; i <= n; i++) {
		scanf("%lld", &a[i]);
	}
	for (int i = 1; i <= m; i++) {
		scanf("%d%d", &b[i].first, &b[i].second);
	}
	sort(a + 1, a + 1 + n);
	for (int i = 1; i <= n; i++)
		sum[i] = sum[i - 1] + a[i];
	for (int i = 1; i <= k; i++) {
		dp[i] = sum[i];
		for (int j = 1; j <= m; j++) {
			if(i >= b[j].first) {
				dp[i] = min(dp[i], dp[i - b[j].first] + sum[i] - sum[i - b[j].first + b[j].second]);
			}
		}
	}
	printf("%lld\n", dp[k]);	 
}

  

猜你喜欢

转载自www.cnblogs.com/pkgunboat/p/10734976.html