Description
硬币购物一共有4种硬币。面值分别为c1,c2,c3,c4。某人去商店买东西,去了tot次。每次带di枚ci硬币,买si的价值的东西。请问每次有多少种付款方法。
Input
第一行 c1,c2,c3,c4,tot 下面tot行 d1,d2,d3,d4,s
Output
每次的方法数
Sample Input
1 2 5 10 2
3 2 3 1 10
1000 2 2 2 900
3 2 3 1 10
1000 2 2 2 900
Sample Output
4
27
27
HINT
数据规模
di,s<=100000
tot<=1000
题解:因为数据范围比较大,所以考虑一下其他的方法。
然后就可以容斥一下。。
先处理出在没有硬币个数限制的情况下买价格为i的物品的方案数,记为f[i];
最后 ans=没有限制的方案数-至少有一个超过限制的方案数+至少有两个超过限制的方案数-至少有三个超过限制的方案数+全部超过限制的方案数。
对于超过限制的方案数的求法,我们可以这么考虑。先强制这超过限制的硬币选d+1个,看一下总和(temp)是不是会超过s,如果超过答案为0,否则答案为f[s-temp];
时间复杂度 O(s+tot);
#include<cstdio>
#include<cstring>
#include<iostream>
#define N 100010
using namespace std;
int n,s,c[5],d[5];
long long f[N],ans;
long long cal(int x,int y,int p,int q)
{
int temp(0);
temp=c[x]*(d[x]+1)+c[y]*(d[y]+1)+c[p]*(d[p]+1)+c[q]*(d[q]+1);
return (temp>s?0:f[s-temp]);
}
int main()
{
for(int i=1;i<=4;i++) scanf("%d",&c[i]);
scanf("%d",&n);
f[0]=1;
for(int j=1;j<=4;j++)
for(int i=c[j];i<N;i++)
f[i]+=f[i-c[j]];
for(int k=1;k<=n;k++)
{
for(int i=1;i<=4;i++)scanf("%d",&d[i]);
scanf("%d",&s);
ans=cal(0,0,0,0);
ans-=cal(1,0,0,0);ans-=cal(2,0,0,0);
ans-=cal(3,0,0,0);ans-=cal(4,0,0,0);
ans+=cal(1,2,0,0);ans+=cal(1,3,0,0);
ans+=cal(1,4,0,0);ans+=cal(2,3,0,0);
ans+=cal(2,4,0,0);ans+=cal(3,4,0,0);
ans-=cal(1,2,3,0);ans-=cal(1,2,4,0);
ans-=cal(1,3,4,0);ans-=cal(2,3,4,0);
ans+=cal(1,2,3,4);
printf("%lld\n",ans);
}
}