1042: [HAOI2008]硬币购物
Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 1835 Solved: 1074
[ Submit][ Status][ Discuss]
Description
硬币购物一共有4种硬币。面值分别为c1,c2,c3,c4。某人去商店买东西,去了tot次。每次带di枚ci硬币,买s
i的价值的东西。请问每次有多少种付款方法。
Input
第一行 c1,c2,c3,c4,tot 下面tot行 d1,d2,d3,d4,s,其中di,s<=100000,tot<=1000
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
容斥原理+背包,思路好题
用完全背包可以求出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);
}
}