【BZOJ 1042】 [HAOI2008]硬币购物

来源:互联网 发布:linux 修改密码 编辑:程序博客网 时间:2024/04/26 06:35

1042: [HAOI2008]硬币购物

Time Limit: 10 Sec  Memory Limit: 162 MB
Submit: 1175  Solved: 697
[Submit][Status]

Description

硬币购物一共有4种硬币。面值分别为c1,c2,c3,c4。某人去商店买东西,去了tot次。每次带di枚ci硬币,买si的价值的东西。请问每次有多少种付款方法。

Input

第一行 c1,c2,c3,c4,tot 下面tot行 d1,d2,d3,d4,s

Output

每次的方法数

Sample Input

1 2 5 10 2
3 2 3 1 10
1000 2 2 2 900

Sample Output

4
27

HINT

数据规模

di,s<=100000

tot<=1000


dp+容斥原理。


首先可以预处理f[i]表示在四种硬币没有限制的情况下买i的东西有几种付款方式。f[i]=sigma(f[i-c[k]]) (1<=k<=4)

 

一定要注意:一种硬币算完再算第二种!!!

即for (int i=1;i<=4;i++) for (int j=1;j<=maxcost;j++)!!

因为反过来就会有重复!!!


接下来就可以容斥了:

得到面值S的超过限制的方案数 - 第1种硬币超过限制的方案数 - 第2种硬币超过限制的方案数 - 第3种硬币超过限制的方案数 - 第4种硬币超过限制的方案数 + 第1,2种硬币同时超过限制的方案数 + 第1,3种硬币同时超过限制的方案数 + ...... + 第1,2,3,4种硬币全部同时超过限制的方案数。

(from byvoid)


#include <iostream>#include <cstdio>#include <algorithm>#include <cstring>#include <cstdlib>#define M 100005#define LL long longusing namespace std;LL f[M],ans;int d[5],c[5],t,s;LL F(int x){return x>=0?f[x]:0LL;}void RC(){for (int i=1;i<=4;i++)ans-=F(s-c[i]*(d[i]+1));for (int i=1;i<=4;i++)for (int j=i+1;j<=4;j++)ans+=F(s-c[i]*(d[i]+1)-c[j]*(d[j]+1));int now;for (int i=1;i<=4;i++){now=0;for (int j=1;j<=4;j++)if (j!=i) now+=c[j]*(d[j]+1);ans-=F(s-now);}ans+=F(s-now-c[4]*(d[4]+1));}int main(){        scanf("%d%d%d%d%d",&c[1],&c[2],&c[3],&c[4],&t);f[0]=1;for (int j=1;j<=4;j++)for (int i=1;i<=100000;i++)if (i>=c[j]) f[i]+=f[i-c[j]];while (t--){for (int i=1;i<=4;i++)scanf("%d",&d[i]);scanf("%d",&s);ans=f[s];RC();printf("%lld\n",ans);}return 0;}



感悟:

1.因为只有四种面值的硬币,所以容斥非常好算


2.一定要注意dp的循环嵌套,防止重复!!


3.byvoid题解


0 0
原创粉丝点击