bzoj1042[HAOI2008]硬币购物
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
题解
我们让每个硬币都有无限个,dp[i]为总价值为i的物品可以用多少种方法表示。则dp[j]=Σ(dp[j-c[i])(1≤i≤4),其中dp[0]=1。记dp[sum]为全集U。任一硬币超限的数量为一种超限的情况-两种超限+三种超限-四种超限。将全集减去这些情况即为答案。因此我们只需容斥一下即为答案。若第i种硬币超限,则只需要先用掉(d[i]+1)枚硬币,剩下的可以任意分配。所以只需得到dp[sum-c[i]*(d[i]+1)]即可。
代码
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cstdlib> 5 #include<algorithm> 6 #include<cmath> 7 #define ll long long 8 using namespace std; 9 ll dp[100010]; 10 ll ans; 11 int tot; 12 int c[10],d[10]; 13 void dfs(int t,int opr,int sum){ 14 if(sum<0) return ; 15 if(t==5){ 16 if(opr&1) ans-=dp[sum]; 17 else ans+=dp[sum]; 18 return ; 19 } 20 dfs(t+1,opr+1,sum-(d[t]+1)*c[t]); 21 dfs(t+1,opr,sum); 22 } 23 int main(){ 24 int i,j; 25 for(i=1;i<=4;++i){ 26 scanf("%d",&c[i]); 27 } 28 scanf("%d",&tot); 29 dp[0]=1; 30 for(i=1;i<=4;++i){ 31 for(j=c[i];j<=100010;++j){ 32 dp[j]+=dp[j-c[i]]; 33 } 34 } 35 for(i=1;i<=tot;++i){ 36 for(j=1;j<=4;++j){ 37 scanf("%d",&d[j]); 38 } 39 int s; 40 scanf("%d",&s); 41 ans=0; 42 dfs(1,0,s); 43 printf("%lld\n",ans); 44 } 45 return 0; 46 }