[bzoj1042][HAOI2008]硬币购物
有三种硬币,每种有自己的币值。
然后有n次询问,每次都给出每种硬币的数量和要付的钱s,求有多少种付法。n<=1000 s<=100000
------
不考虑限制,就是个简单dp....
有限制的时候,我们可以考虑反过来用总的方案数量剪掉不合法的。
根据容斥原理,不合法的情况=
有1种硬币数量不合法即第1种不合法+第2+第3+第4 再去掉有两个不合法的12种都不合法-23种都不合法-34种都不合法-........加上三种不合法的即123种不合法+124不合法... 最后减去1234都不合法。
所以每次询问都这样做一遍,让一种硬币不合法只要付比他的限制多一个就可以了,剩下的钱可以随意付,直接去dp里查值。
复杂度4*s+n*2^4
#include<iostream> #include<cstdio> #define ll long long using namespace std; 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 ans; int c[5],x[5],n,s; ll f[100005]; void dfs(int i,int p,int sum) { // printf("%d %d %d\n",i,p,sum); if(i>4){ans+=p*f[sum];return;} dfs(i+1,p,sum); if(sum>=c[i]*(x[i]+1))dfs(i+1,-p,sum-c[i]*(x[i]+1)); } int main() { for(int i=1;i<=4;i++)c[i]=read(); f[0]=1; for(int i=1;i<=4;i++) for(int j=c[i];j<=100000;j++) f[j]+=f[j-c[i]]; n=read(); for(int i=1;i<=n;i++) { for(int j=1;j<=4;j++)x[j]=read();s=read();ans=0; dfs(1,1,s); printf("%lld\n",ans); } return 0; }
FallDream代表秋之国向您问好!
欢迎您来我的博客www.cnblogs.com/FallDream