[HAOI2008]硬币购物——[DP+容斥]

该博客探讨了HAOI2008竞赛中的一道硬币购物问题,介绍了如何使用动态规划(DP)和容斥原理解决这个问题。首先,博主通过完全背包算法处理无限制情况,然后通过补集思想计算超额硬币的方案数。通过位运算枚举子集,博主详细解释了如何计算超额一种、两种、三种和四种硬币的方案,并给出了代码实现。
摘要由CSDN通过智能技术生成

【题目描述】

硬币购物一共有4种硬币。面值分别为c1,c2,c3,c4。某人去商店买东西,去了tot次。每次带di枚ci硬币,买si的价值的东西。请问每次有多少种付款方法。

【输入格式】

第一行 c1,c2,c3,c4,tot 下面tot行 d1,d2,d3,d4,s

【输出格式】

每次的方法数

S a m p l e    I n p u t Sample~~Input Sample  Input

1 2 5 10 2
3 2 3 1 10
1000 2 2 2 900

S a m p l e    O u t p u t Sample~~Output Sample  Output

4
27

【题意分析】

首先做一遍背包是显然的,但这样肯定会t

然后我们可以用容斥我当然不会容斥

先用完全背包处理出没有任何限制时的方案数,然后我们利用补集思想

设总价值为 t o t tot tot,第 i i i种硬币价值 v [ i ] v[i] v[i],个数 c [ i ] c[i] c[i]

那么超额第i种硬币的方案数就存在 d p [ t o t − v [ i ] ∗ ( c [ i ] + 1 ) ] dp[tot-v[i]*(c[i]+1)] dp[totv[i](c[i]+1)]中,减掉即可

为什么?因为硬币只有 c [ i ] c[i] c[i]个,要超额就得从 c [ i ] + 1 c[i]+1 c[i]+1个开始。

还有超额两种、三种、四种的情况,怎么办?

脑子yy一下 拿出韦恩图,一画就非常清晰了

d p [ t o t ] dp[tot] dp[tot]存的是理想情况,最后答案就是 d p [ t o t ] dp[tot] dp[tot]减去超额一种、三种的所有情况,再加上超额两种、四种的所有情况

至于位运算枚举子集,四元容斥还没必要,手写个算了

Code:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
#define int long long
#define MAXN 100010
using namespace std;

int v[MAXN], a[MAXN], dp[MAXN];

inline int read () {
	register int s = 0, w = 1;
	register char ch = getchar ();
	while (! isdigit (ch)) {if (ch == '-') w = -1; ch = getchar ();}
	while (isdigit (ch)) {s = (s << 3) + (s << 1) + (ch ^ 48); ch = getchar ();}
	return s * w;
}

inline int calc (int x) {
	return v[x] * (a[x] + 1);
}

signed main () {
	for (register int i = 1; i <= 4; i++) v[i] = read ();
	dp[0] = 1;
	for (register int i = 1; i <= 4; i++)
		for (register int j = v[i]; j <= MAXN; j++)
			dp[j] += dp[j - v[i]];
	int q = read ();
	for (register int I = 1; I <= q; I++) {
		for (register int i = 1; i <= 4; i++) a[i] = read ();
		int m = read (), ans = dp[m];
		int p = calc (1), q = calc (2), r = calc (3), s = calc (4);
		if (m >= p) ans -= dp[m - p]; if (m >= q) ans -= dp[m - q];
		if (m >= r) ans -= dp[m - r]; if (m >= s) ans -= dp[m - s];
		if (m >= p + q) ans += dp[m - p - q]; if (m >= p + r) ans += dp[m - p - r];
		if (m >= p + s) ans += dp[m - p - s]; if (m >= q + r) ans += dp[m - q - r];
		if (m >= q + s) ans += dp[m - q - s]; if (m >= r + s) ans += dp[m - r - s];
		if (m >= p + q + r) ans -= dp[m - p - q - r];
		if (m >= p + q + s) ans -= dp[m - p - q - s];
		if (m >= p + r + s) ans -= dp[m - p - r - s];
		if (m >= q + r + s) ans -= dp[m - q - r - s];
		if (m >= p + q + r + s) ans += dp[m - p - q - r - s];
		printf ("%lld\n", ans);
	}
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值