题目大意:4种面值c[i]的硬币,每种硬币持有d[i]个,问有多少种方法支付出正好N块钱。
可以先预处理出持有硬币无限的情况dp[n],即一个完全背包问题。
之后根据容斥原理,相当于求但是拥有限制
,可以参考有限制的不定方程非负整数解的容斥方法,我们设全集
为所有在无限情况下凑出S的方案数,属性为
,那么就可以对所有补集的并用容斥原理展开进行计算,对于每个
是由具有k个不同反向性质
组成的集合,对应在容斥式子中的答案就是在无限情况下凑出
的方案数即
。
最后用全集减去就可以了。
#include<bits/stdc++.h>
#include<unordered_map>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
//#define int LL
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#pragma warning(disable :4996)
const int maxn = 100010;
const int mod = 1e9 + 7;
const double eps = 1e-8;
LL c[5], N;
LL d[5], S;
LL dp[maxn];
void solve()
{
memset(dp, 0, sizeof(dp));
dp[0] = 1;//价格为0时无限硬币组成之方法数
for (LL i = 1; i <= 4; i++)
{
for (LL j = 0; j <= S; j++)
{
if (j - c[i] >= 0)
dp[j] += dp[j - c[i]];
}
}
LL ans = 0;
for (LL i = 1; i < 16; i++)//枚举集合数1的个数
{
LL tmp = S, bit = 0;//1的个数
for (LL j = 1; j <= 4; j++)
{
if ((i >> (j - 1)) & 1)//这一位1
{
tmp -= c[j] * (d[j] + 1);
bit++;
}
}
if (tmp >= 0)
ans += (bit % 2 ? 1 : -1) * dp[tmp];//用容斥转化为无限制的完全背包情形
}
cout << dp[S] - ans << endl;
}
int main()
{
IOS;
for (int i = 1; i <= 4; i++)
cin >> c[i];
cin >> N;
for (int i = 0; i < N; i++)
{
for (int j = 1; j <= 4; j++)
cin >> d[j];
cin >> S;
solve();
}
return 0;
}