题意
硬币购物一共有4种硬币。面值分别为c1,c2,c3,c4。某人去商店买东西,去了tot次。每次带di枚ci硬币,买si的价值的东西。请问每次有多少种付款方法。
题解
一眼看去一个多重背包dp...再一眼看去数据范围..1e5这样暴力背包就t了.
可以转换想法,假设硬币的枚数没有限制,这样在每一组样例中,用不限制的总的数量减掉不满足条件的数量。
而不限制硬币个数的数量就可以用多重背包求解即可
for(ll i = 0;i < 4;++i)
for(ll j = 0;j < maxn;++j)
if(j - c[i] >= 0) dp[j] += dp[j-c[i]]; 接下来的问题就是如何找到针对每组数据不满足条件的付款方法,用总数减去即可。
考虑容斥定理,即,对于四种硬币可以看做四个集合,比如:即表示至少使用d0枚硬币买下东西,而满足条件情况的就是用所有情况减掉至少使用(d0+1)枚。但是考虑到,当减掉某两种不满足条件的情况时,我们将两集合的交集重复减掉了两遍,即需要使用容斥定理,将交集加回来。三种四种时同样。
而如何遍历这几种情况呢,可以将状态压缩为2进制表示,即1001就代表不满足第一种硬币和第四种硬币的情况,这样一共15种情况就可以表示为0001-1111 即遍历1-15,判断其二进制位即可。
code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll maxn = 1e5 + 10;
ll dp[maxn];
int main()
{
ll c[4];
ll t;
for(ll i = 0;i < 4;++i)cin>>c[i];
cin>>t;
dp[0] = 1;
for(ll i = 0;i < 4;++i)
for(ll j = 0;j < maxn;++j)
if(j - c[i] >= 0) dp[j] += dp[j-c[i]];
for(ll i = 0;i < t;++i){
ll cnt[4];
for(ll i = 0;i < 4;++i) scanf("%d",&cnt[i]);
ll s;
cin>>s;
ll res = dp[s];
for(ll i = 1;i <= 15;++i){
ll tmp = 0;
ll now = 0;
for(ll j = 0;j < 4;++j){
if((i >> j) & 1){
tmp++;
now += (cnt[j] + 1) * c[j];
}
}
ll base = 1;
if(tmp & 1) base = -1;
if(s-now >= 0)res += base * dp[s - now];
}
printf("%lld\n",res);
}
return 0;
} 
京公网安备 11010502036488号