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

Sample Output

4
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 }

 

posted @ 2018-06-20 14:15  lazytear  阅读(...)  评论(... 编辑 收藏