[BZOJ 1042][HAOI2008]硬币购物
2016-03-14 21:25
302 查看
1042: [HAOI2008]硬币购物
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
Sample Output
4
27
数据规模
di,s<=100000
tot<=1000
容斥原理+背包。
byvoid:
设F[i]为不考虑每种硬币的数量限制的情况下,得到面值i的方案数。则状态转移方程为
F[i]=Sum{F[i-C[k]] | i-C[k]>=0 且 k=1..4}
为避免方案重复,要以k为阶段递推,边界条件为F[0]=1,这样预处理的时间复杂度就是O(S)。
接下来对于每次询问,奇妙的解法如下:根据容斥原理,答案为 得到面值S的超过限制的方案数 – 第1种硬币超过限制的方案数 – 第2种硬币超过限制的方案数 – 第3种硬币超过限制的方案数 – 第4种硬币超过限制的方案数 + 第1,2种硬币同时超过限制的方案数 + 第1,3种硬币同时超过限制的方案数 + …… + 第1,2,3,4种硬币全部同时超过限制的方案数。
当第1种硬币超过限制时,只要要用到D[1]+1枚硬币,剩余的硬币可以任意分配,所以方案数为 F[ S – (D[1]+1)C[1] ],当且仅当(S – (D[1]+1)C[1])>=0,否则方案数为0。其余情况类似,每次询问只用问16次,所以询问的时间复杂度为O(1)。
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
Sample Output
4
27
数据规模
di,s<=100000
tot<=1000
容斥原理+背包。
byvoid:
设F[i]为不考虑每种硬币的数量限制的情况下,得到面值i的方案数。则状态转移方程为
F[i]=Sum{F[i-C[k]] | i-C[k]>=0 且 k=1..4}
为避免方案重复,要以k为阶段递推,边界条件为F[0]=1,这样预处理的时间复杂度就是O(S)。
接下来对于每次询问,奇妙的解法如下:根据容斥原理,答案为 得到面值S的超过限制的方案数 – 第1种硬币超过限制的方案数 – 第2种硬币超过限制的方案数 – 第3种硬币超过限制的方案数 – 第4种硬币超过限制的方案数 + 第1,2种硬币同时超过限制的方案数 + 第1,3种硬币同时超过限制的方案数 + …… + 第1,2,3,4种硬币全部同时超过限制的方案数。
当第1种硬币超过限制时,只要要用到D[1]+1枚硬币,剩余的硬币可以任意分配,所以方案数为 F[ S – (D[1]+1)C[1] ],当且仅当(S – (D[1]+1)C[1])>=0,否则方案数为0。其余情况类似,每次询问只用问16次,所以询问的时间复杂度为O(1)。
#include #include #include #include #define maxn 100010 using namespace std; typedef long long ll; ll ans, dp[maxn]; int c[4], d[4]; inline void read(int& num){ char ch = getchar();num = 0; for(; ch < '!'; ch = getchar()); for(; ch > '!'; ch = getchar()) num = (num << 3) + (num << 1) + (ch ^ 48); } void solve(int num, int dep, ll s){ if(s < 0)return; if(dep == 4){ if(num & 1)ans -= dp[s]; else ans += dp[s]; return; } solve(num + 1, dep + 1, s - (d[dep] + 1) * c[dep]); solve(num, dep + 1, s); } int main(){ int test; for(int i = 0; i < 4; i ++) read(c[i]); read(test); dp[0] = 1; for(int i = 0; i < 4; i ++) for(int j = c[i]; j <= 100000; j ++) dp[j] += dp[j - c[i]]; int x; while(test --){ for(int i = 0; i < 4; i ++) read(d[i]); read(x); ans = 0; solve(0, 0, x); printf("%lld\n", ans); } return 0; }
相关文章推荐
- PHP动态规划解决0-1背包问题实例分析
- PHP贪婪算法解决0-1背包问题实例分析
- PHP回溯法解决0-1背包问题实例分析
- Codeforces Round #198 (Div. 1)
- 4495: Least Prime factor 找到最小质因子P的第N小正整数
- hdu 1203 I NEED A OFFER! 01背包
- HDU 1203 I NEED A OFFER! 题解
- 多重背包问题的单调队列优化 转载
- hdu5072 Coprime 2014鞍山现场赛C题 容斥原理+单色三角
- UGUI中ScrollView滑动适应问题解决办法
- 二维树状数组
- 背包问题(贪婪法)
- zoj3258 DP 01背包
- HDU2191悼念512汶川大地震遇难同胞——珍惜现在,感恩生活
- 【分组背包】开心的金明
- 【01背包】原型
- HDU 1203 i need a offer
- HDU 1695 GCD
- 小P的故事——神奇的饭卡(0—1背包,求剩余钱最少)
- HDU 1494 跑跑卡丁车