[BZOJ 1042][HAOI 2008]硬币购物(背包+容斥原理)

来源:互联网 发布:一楼土木人淘宝靠谱吗 编辑:程序博客网 时间:2024/05/20 19:19

题目链接:http://www.lydsy.com:808/JudgeOnline/problem.php?id=1042

刚开始搞容斥原理,还很有点吃力,我太弱了。。。

首先用被类似于背包的DP进行预处理,假设每种硬币个数无限制,求出f[i]=凑出面值i的方案总数。

但是实际上题目中每种硬币个数是有限制的,设四种硬币分别是a、b、c、d,则凑出面值S的方案中超出限制的方案数=a超出限制的方案数+b超出限制的方案数+c超出限制的方案数+d超出限制的方案数-a和b都超出限制的方案数-a和c都超出限制的方案数......-c和d都超出限制的方案数+a、b、c都超出限制的方案数+a、c、d都超出限制的方案数+a、b、d都超出限制的方案数+b、c、d都超出限制的方案数-abcd都超出限制的方案数

然后对于每一次询问DFS,用容斥原理解决即可,dfs时用k做标记,标志当前情况是加还是减。

注:当dfs第i个硬币时,第i个硬币用了d[i]+1个,就意味着第i个硬币超出了限制,而剩余的硬币就可以随意分配,不受第i个硬币的个数的限制。

#include <iostream>#include <stdio.h>#include <string.h>#include <stdlib.h>#include <algorithm>#define MAXN 100050using namespace std;typedef long long int LL;LL ans=0;LL f[MAXN];int c[5],d[5];void dfs(int x,int k,int sum) //在用第x种硬币,还需要支付sum元的钱,k是容斥原理加还是减的标志{    if(sum<0) return;    if(x==5)    {        if(k&1) ans-=f[sum];        else ans+=f[sum];        return;    }    dfs(x+1,k+1,sum-(d[x]+1)*c[x]);    dfs(x+1,k,sum);}int main(){    for(int i=1;i<=4;i++) scanf("%d",&c[i]);    int T;    scanf("%d",&T);    f[0]=1;    for(int k=1;k<=4;k++) //先预处理出f[i]=所有硬币数量无限的情况下,获得面值i的方案数,为了避免重复计算,以k为阶段递推        for(int i=c[k];i<MAXN;i++)            f[i]+=f[i-c[k]];    for(int i=1;i<=T;i++)    {        for(int i=1;i<=4;i++) scanf("%d",&d[i]);        int x; //要买x元的商品        ans=0;        scanf("%d",&x);        dfs(1,0,x);        printf("%lld\n",ans);    }    return 0;}

0 0
原创粉丝点击