BZOJ 1042 硬币购物
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
3 2 3 1 10
1000 2 2 2 900
Sample Output
4
27
27
HINT
数据规模
di,s<=100000
tot<=1000
暴力就是一个裸的背包问题,因此我们要考虑优化。
我们可以用容斥原理,离线得到所需要求得的答案。
先做一遍背包的dp,我就不多讲了。然后进行容斥,ans=总方案f[sum]-一种不合法(多一张)+两种不合法-三种不合法+四种不合法。
1 #include<cstdio> 2 #include<cstdlib> 3 using namespace std; 4 5 #define maxn 100010 6 int T,c[5],d[5],save[5],s; 7 long long f[maxn],ans; 8 9 inline void ready() 10 { 11 f[0] = 1; 12 for (int i = 1;i <= 4;++i) for (int j = c[i];j < maxn;++j) f[j] += f[j-c[i]]; 13 } 14 15 inline void dfs(int a,int cho) 16 { 17 if (a > 4) 18 { 19 int dec = 0; long long ff; 20 for (int i = 1;i <= cho;++i) 21 { 22 dec += c[save[i]]*(d[save[i]]+1); 23 if (dec > s) return; 24 } 25 if (cho & 1) ff = -1; else ff = 1; 26 ans += ff*f[s-dec]; 27 return; 28 } 29 dfs(a+1,cho); 30 save[cho+1] = a; 31 dfs(a+1,cho+1); 32 } 33 34 int main() 35 { 36 freopen("1042.in","r",stdin); 37 freopen("1042.out","w",stdout); 38 for (int i = 1;i <= 4;++i) scanf("%d",c+i); 39 scanf("%d",&T); 40 ready(); 41 while (T--) 42 { 43 for (int i = 1;i <= 4;++i) scanf("%d",d+i); 44 scanf("%d",&s); 45 ans = 0; dfs(1,0); 46 printf("%lld\n",ans); 47 } 48 return 0; 49 }
高考结束,重新回归。