• [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;
    }
  • 相关阅读:
    How to install Lineage Os ROM on any Android device [2 methods]
    2017 年不可错过的开发工具 Top 50
    (OK) 华为全网通 honor 5x
    (OK) (solved) How restore /cust partition
    (OK) 华为全网通 honor 5x
    (OK) 华为全网通 honor 5x
    Chris Lattner
    (OK) 救砖:华为全网通 honor 5x
    (OK) Linux 平台 CUST.zip 制作方法
    (OK) Linux 下解包华为固件包UPDATE.APP
  • 原文地址:https://www.cnblogs.com/FallDream/p/bzoj1042.html
Copyright © 2020-2023  润新知