【题目描述】
硬币购物一共有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[tot−v[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);
}
}