Bzoj1042 [HAOI2008]硬币购物
Submit: 2468 Solved: 1478
Description
硬币购物一共有4种硬币。面值分别为c1,c2,c3,c4。某人去商店买东西,去了tot次。每次带di枚ci硬币,买s
i的价值的东西。请问每次有多少种付款方法。
Input
第一行 c1,c2,c3,c4,tot 下面tot行 d1,d2,d3,d4,s,其中di,s<=100000,tot<=1000
Output
每次的方法数
Sample Input
1 2 5 10 2
3 2 3 1 10
1000 2 2 2 900
3 2 3 1 10
1000 2 2 2 900
Sample Output
4
27
27
HINT
Source
数学问题 DP 容斥
思路好神奇啊。
设f[i]表示不考虑硬币数量限制的情况下,凑出面值 i 的方案数。一个简单的背包就统计出来了。
然后根据容斥原理统计:
ans=不考虑数量限制凑出S的方案数 - c1超限制凑出S的方案数 - c2超限制凑出S的方案数 - c3超限制凑出S的方案数 - c4超限制凑出S的方案数 + c1和c2超限凑出S的方案数 +... +c1 c2 c3 c4均超限凑出S的方案数。
c1超出限制凑出S的方案数= f[S-(d[1]+1)*c[1]] (c1至少超出了1枚,剩余面值可以用任意硬币)
其他类似。
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 #include<queue> 6 #define LL long long 7 using namespace std; 8 const int mxn=100010; 9 int read(){ 10 int x=0,f=1;char ch=getchar(); 11 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 12 while(ch>='0' && ch<='9'){x=x*10-'0'+ch;ch=getchar();} 13 return x*f; 14 } 15 int c[5],d[5],S; 16 LL f[mxn]; 17 inline LL F(int x){return x<0?0:f[x];} 18 void calc(){ 19 LL ans=F(S); 20 for(int i=1;i<=4;i++) 21 ans-=F(S-(d[i]+1)*c[i]); 22 for(int i=1;i<=4;i++) 23 for(int j=i+1;j<=4;j++) 24 ans+=F(S-(d[i]+1)*c[i]-(d[j]+1)*c[j]); 25 ans-=F( S-(d[1]+1)*c[1]-(d[2]+1)*c[2]-(d[3]+1)*c[3] ); 26 ans-=F( S-(d[1]+1)*c[1]-(d[2]+1)*c[2]-(d[4]+1)*c[4] ); 27 ans-=F( S-(d[1]+1)*c[1]-(d[3]+1)*c[3]-(d[4]+1)*c[4] ); 28 ans-=F( S-(d[2]+1)*c[2]-(d[3]+1)*c[3]-(d[4]+1)*c[4] ); 29 ans+=F( S-(d[1]+1)*c[1]-(d[2]+1)*c[2]-(d[3]+1)*c[3]-(d[4]+1)*c[4] ); 30 printf("%lld\n",ans); 31 return; 32 } 33 int main(){ 34 int i,j; 35 f[0]=1; 36 for(i=1;i<=4;i++){ 37 c[i]=read(); 38 for(j=c[i];j<mxn;j++)f[j]+=f[j-c[i]]; 39 } 40 int T=read(); 41 while(T--){ 42 for(i=1;i<=4;i++)d[i]=read(); 43 S=read(); 44 calc(); 45 } 46 return 0; 47 }
本文为博主原创文章,转载请注明出处。