版权声明:转载请注明出处。 https://blog.csdn.net/TungstenC/article/details/83140997
题目描述
硬币购物一共有 种硬币。面值分别为 。某人去商店买东西,去了 次。每次带 枚 硬币,买 的价值的东西。请问每次有多少种付款方法。 , 。
算法分析
并不会多重背包,有空再去学,然而多重背包貌似也不能做。
先考虑没有限制的情况,就是一个完全背包,设已计算出的用前 种硬币购买价值 的物品的方案数为 ,则仅给第 种硬币加上选取个数满足 限制时的方案数为 。
为什么呢?给第 种硬币加上这种限制后使用该种硬币的数量不得超过 ,考虑补集转换,我们可以先让它先不使用 枚该种硬币,最后再使用 枚该种金币,即 ,这样无论之前怎么选都能保证选择的硬币个数大于 ,最后再用总的方案数减去不满足条件的方案数就是给第 种硬币加上限制的方案数。
注意到,如果我们给多种硬币加上限制,可能会有重复减去的部分,即该方案既满足使用硬币 的数量大于 又满足使用硬币 的数量大于 ,它的方案数等于 ,将多减的部分加回去就可以了。这实际上使用的是容斥原理,可以用位运算的方法方便的计算出来。
注意要开 位整数保存结果。
代码实现
#include <cstdio>
typedef long long int ll;
int c[4],d[4];ll f[100005],sub[4];
int main() {
f[0]=1;
for(int i=0;i<4;++i) {
scanf("%d",&c[i]);
for(int j=c[i];j<=100000;++j) f[j]+=f[j-c[i]];
}
int tot;scanf("%d",&tot);
while(tot--) {
for(int i=0;i<4;++i) {
scanf("%d",&d[i]);
sub[i]=c[i]*(d[i]+1LL);
}
int s;scanf("%d",&s);ll ans=0;
for(int i=0;i<(1<<4);++i) {
int cnt=0;ll t=0;
for(int j=0;j<4;++j) if(i>>j&1) {
++cnt;t+=sub[j];
}
if(s-t>=0) (cnt&1)?ans-=f[s-t]:ans+=f[s-t];
}
printf("%lld\n",ans);
}
return 0;
}