BZOJ1042:硬币购物(背包 & 容斥)
来源:互联网 发布:淘宝比熊犬多少钱 编辑:程序博客网 时间:2024/05/20 19:48
1042: [HAOI2008]硬币购物
Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 2953 Solved: 1822
[Submit][Status][Discuss]
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
思路:很巧妙的题目,一般我们是直接完全背包那样求方案数的,但是这样会超时,考虑到硬币种类不多,应该先算出不限硬币数目的方案数,再利用容斥原理去掉非法的方案数,b[i]+1就是第i种硬币非法的状态了。
# include <iostream># include <cstdio>using namespace std;typedef long long LL;const int maxn = 1e5;LL dp[maxn+30]={1}, a[5], b[5], s, ans;void dfs(int pos, LL sum, int cnt){ if(sum < 0) return; if(pos == 5) { if(cnt&1) ans -= dp[sum]; else ans += dp[sum]; return; } dfs(pos+1, sum, cnt); dfs(pos+1, sum-(b[pos]+1)*a[pos], cnt+1);}int main(){ int t; for(int i=1; i<=4; ++i) scanf("%lld",&a[i]); for(int i=1; i<=4; ++i) for(int j=0; j+a[i]<=maxn; ++j) dp[j+a[i]] += dp[j]; scanf("%d",&t); while(t--) { for(int i=1; i<=4; ++i) scanf("%lld",&b[i]); scanf("%lld",&s); ans = 0; dfs(1, s, 0); printf("%lld\n",ans); } return 0;}
阅读全文