[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
",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
",ans);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/FallDream/p/bzoj1042.html