标签:blog http io ar os sp for on 2014
题目链接: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 100050 using 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; }
[BZOJ 1042][HAOI 2008]硬币购物(背包+容斥原理)
标签:blog http io ar os sp for on 2014
原文地址:http://blog.csdn.net/qpswwww/article/details/41731273