硬币购物一共有4种硬币。面值分别为c1,c2,c3,c4。某人去商店买东西,去了tot次。每次带di枚ci硬币,买s
i的价值的东西。请问每次有多少种付款方法。
硬币购物一共有4种硬币。面值分别为c1,c2,c3,c4。某人去商店买东西,去了tot次。每次带di枚ci硬币,买s
i的价值的东西。请问每次有多少种付款方法。
第一行 c1,c2,c3,c4,tot 下面tot行 d1,d2,d3,d4,s,其中di,s<=100000,tot<=1000
每次的方法数
容斥原理+背包,思路好题
用完全背包可以求出f[i],表示不限定硬币数量,组成i的方案数。
直接求不好求,可以运用补集思想,ans=不限定硬币的方案数-有硬币超出的方案数。
那有硬币超出的方案数sum怎么求呢?
根据容斥原理有:sum=至少一种超出的方案数-至少两种超出的方案数+至少三种超出的方案数-全部超出的方案数。
然后对于每一步,某几种硬币至少超出限定数量,也就是至少有d[i]+1个,剩余的价值rest=s-∑(c[i]*(d[i]+1)),方案数即为f[rest],f数组已经用完全背包预处理了。
#include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> #include<cstring> #include<algorithm> #define F(i,j,n) for(int i=j;i<=n;i++) #define D(i,j,n) for(int i=j;i>=n;i--) #define ll long long #define maxn 100005 using namespace std; int tot,n,c[10],d[10]; ll ans,f[maxn]; inline int read() { int x=0,f=1;char ch=getchar(); while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();} while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } ll get(int x1=0,int x2=0,int x3=0,int x4=0) { int sum=(d[x1]+1)*c[x1]+(d[x2]+1)*c[x2]+(d[x3]+1)*c[x3]+(d[x4]+1)*c[x4]; return sum<=n?f[n-sum]:0; } int main() { F(i,1,4) c[i]=read();tot=read(); f[0]=1; F(i,1,4) F(j,c[i],100000) f[j]+=f[j-c[i]]; while (tot--) { F(i,1,4) d[i]=read();n=read(); ans=get(); ans-=get(1)+get(2)+get(3)+get(4); ans+=get(1,2)+get(1,3)+get(1,4)+get(2,3)+get(2,4)+get(3,4); ans-=get(2,3,4)+get(1,3,4)+get(1,2,4)+get(1,2,3); ans+=get(1,2,3,4); printf("%lld\n",ans); } }