【bzoj1042】[HAOI2008]硬币购物

首先使用DP预处理,先求出,在不考虑每种硬币个数的限制的情况下,每个钱数有多少种拼凑方案。

为了避免重复的方案被转移,所以我们以硬币种类为第一层循环,这样阶段性的增加硬币。

一定要注意这个第一层循环要是硬币种类,并且初始 f[0] = 1。

之后对于每个询问 (A1, A2, A3, A4, S) ,根据容斥原理,我们要求的答案 Ans 就是 f[S] - (硬币1超限制的方案数) - (硬币2超限制的方案数) - (硬币3超限制的方案数) - (硬币4超限制的方案数) + (硬币1,2超限制的方案数) + (硬币1,3超限制的方案数) + (硬币1,4超限制的方案数) + .... - (硬币1,2,3超限制的方案数) - ... + (硬币1,2,3,4超限制的方案数) 。

怎样求硬币1超限制的方案数呢?我们只要先固定取 (A1+1) 个硬币1,剩余的钱数随便取就可以了,就是 f[S - (A1+1) * V[1]] 。

其余的情况都类似。

容斥的部分使用搜索实现。

 

 
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
using namespace std;
 
#define MAXN 100010
 
typedef long long LL;
 
int n;
int x;
int a[7],b[7];
 
LL ans;
 
LL f[MAXN];
 
void dfs(int x,int k,int d)
{
    if (d<0)
        return ;
    if (x==5)
    {
        if (k & 1)
            ans-=f[d];
        else
            ans+=f[d];
        return ;
    }
    dfs(x+1,k+1,d-(a[x]+1)*b[x]);
    dfs(x+1,k,d);
}
 
int main()
{
    for (int i=1;i<=4;i++)
        scanf("%d",&b[i]);
    scanf("%d",&n);
    f[0]=1;
    for (int i=1;i<=4;i++)
        for (int j=b[i];j<=MAXN;j++)
            f[j]+=f[j-b[i]];
    for (int i=1;i<=n;i++)
    {
        for (int j=1;j<=4;j++)
            scanf("%d",&a[j]);
        scanf("%d",&x);
        ans=0;
        dfs(1,0,x);
        printf("%lld\n",ans);
    }
    return 0;
}

  

 
还有一个鬼畜算法。。搞不清楚啊。。

 

用容斥原理做背包。

首先,我们要先处理出四种钞票都不限的方案数。

对于每一个询问,我们利用容斥原理,答案为:得到S所有超过数量限制的方案数-硬币1超过限制的方案数-硬币2超过限制的方案数-硬币3超过限制的方案数-硬币4超过限制的方案数+硬币1、2超过限制的方案数+…+硬币1、2、3、4均超过限制的方案数。

而对于每种方案数的求法,也非常简单:假设我们要求的是F[S],则硬币1超过限制(即硬币1取的个数≥d[1]+1,不考虑硬币2、3、4是否超过限制)时的方案数即为F[S-(d[1]+1)×c[1]]。

 
#include <iostream> 
#include <cstdio> 
#include <cstring> 
#include <algorithm> 
using namespace std; 
int c[5]; 
long long F[110000]; 
struct{long long operator[](int pos){return pos<0?0:F[pos];}}f; 
int main(int argc, char *argv[]) 
{ 
    int T;scanf("%d%d%d%d%d",&c[1],&c[2],&c[3],&c[4],&T); 
    F[0]=1; 
    for(int i=1;i<=4;i++) 
        for(int j=0;j<=100000;j++) 
        if(j+c[i]<=100000)F[j+c[i]]+=F[j]; 
    while(T--) 
    { 
        int d[5],s;scanf("%d%d%d%d%d",&d[1],&d[2],&d[3],&d[4],&s); 
        long long ans=f[s]; 
        ans-=f[s-(d[1]+1)*c[1]]; 
        ans-=f[s-(d[2]+1)*c[2]]; 
        ans-=f[s-(d[3]+1)*c[3]]; 
        ans-=f[s-(d[4]+1)*c[4]]; 
        ans+=f[s-(d[1]+1)*c[1]-(d[2]+1)*c[2]]; 
        ans+=f[s-(d[1]+1)*c[1]-(d[3]+1)*c[3]]; 
        ans+=f[s-(d[1]+1)*c[1]-(d[4]+1)*c[4]]; 
        ans+=f[s-(d[2]+1)*c[2]-(d[3]+1)*c[3]]; 
        ans+=f[s-(d[2]+1)*c[2]-(d[4]+1)*c[4]]; 
        ans+=f[s-(d[3]+1)*c[3]-(d[4]+1)*c[4]]; 
        ans-=f[s-(d[1]+1)*c[1]-(d[2]+1)*c[2]-(d[3]+1)*c[3]]; 
        ans-=f[s-(d[1]+1)*c[1]-(d[2]+1)*c[2]-(d[4]+1)*c[4]]; 
        ans-=f[s-(d[1]+1)*c[1]-(d[3]+1)*c[3]-(d[4]+1)*c[4]]; 
        ans-=f[s-(d[2]+1)*c[2]-(d[3]+1)*c[3]-(d[4]+1)*c[4]]; 
        ans+=f[s-(d[1]+1)*c[1]-(d[2]+1)*c[2]-(d[3]+1)*c[3]-(d[4]+1)*c[4]]; 
        #ifdef ONLINE_JUDGE 
            printf("%lld\n",ans); 
        #else 
            printf("%I64d\n",ans); 
        #endif 
    } 
    return 0; 
} 
 

  

貌似更快一些= =
posted @ 2016-03-25 21:04  Yangjiyuan  阅读(234)  评论(0编辑  收藏  举报