洛谷 P1450 HAOI 2008 硬币购物
题目描述
硬币购物一共有4种硬币。面值分别为c1,c2,c3,c4。某人去商店买东西,去了tot次。每次带di枚ci硬币,买si的价值的东西。请问每次有多少种付款方法。
输入格式
第一行 c1,c2,c3,c4,tot 下面tot行 d1,d2,d3,d4,s
输出格式
每次的方法数
说明
di,s<=100000
tot<=1000
样例
输入
1 2 5 10 2
3 2 3 1 10
1000 2 2 2 900
输出
4
27
这个题很容易可以想到用多重背包去解决,但是时间不允许呀!!!
考虑用完全背包代替多重背包,首先完全背包求出没有硬币限制的凑成 s 的方案数,然后陷入沉思,然后用无限制的方案数减去不合法的(限制条件)方案数就是结果。
考虑不合法的方案数,有四种硬币,考虑每一个硬币是否数量超了,有15种可能,1 2 3 4分别超过了,1 和 2, 2 和 3 ......很明显可以用容斥原理解决此题,不合法的方案数就是:
超出一个硬币的方案数 - 超出两个的方案数 + 超出三个的方案数 - 超出四个的方案数
再用总方案数dp[S]减去上述值即可。
再考虑不合法的方案数怎么求???
不合法有四种可能,上面说了,分别是超出一个,两个......对于每一种,假设1不合法,那么在完全背包中,1至少被放入了d[1] + 1次,我们多选一个硬币1,那么现在硬币1有d[1] + 1个了,一定超额了,那么剩余的背包空间无论怎么填,硬币1都会超额,所以硬币1超额的方案数就是 dp[S - c[i] * (d[i]) + 1],然后就是容斥原理大法了
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int c[6], d[6], tot, s;
ll res;
const int N = 1e5 + 10;
ll dp[N];
ll cal(ll n){
return c[n] * d[n] + c[n];
}
int main()
{
scanf("%d %d %d %d %d", &c[1], &c[2], &c[3], &c[4], &tot);
while (tot--){
scanf("%d %d %d %d %d", &d[1], &d[2], &d[3], &d[4], &s);
memset(dp, 0, sizeof (dp));
dp[0] = 1;
for (int i = 1; i <= 4; i++){
for (int j = c[i]; j <= s; j++){
dp[j] += dp[j - c[i]];
}
}
ll res = dp[s];
if(cal(1) <= s)res -= dp[s - cal(1)];
if(cal(2) <= s)res -= dp[s - cal(2)];
if(cal(3) <= s)res -= dp[s - cal(3)];
if(cal(4) <= s)res -= dp[s - cal(4)];
if(cal(1) + cal(2) <= s)res += dp[s - cal(1) - cal(2)];
if(cal(1) + cal(3) <= s)res += dp[s - cal(1) - cal(3)];
if(cal(1) + cal(4) <= s)res += dp[s - cal(1) - cal(4)];
if(cal(3) + cal(2) <= s)res += dp[s - cal(3) - cal(2)];
if(cal(4) + cal(2) <= s)res += dp[s - cal(4) - cal(2)];
if(cal(3) + cal(4) <= s)res += dp[s - cal(3) - cal(4)];
if(cal(1) + cal(2) + cal(3) <= s)res -= dp[s - cal(1) - cal(2) - cal(3)];
if(cal(1) + cal(2) + cal(4) <= s)res -= dp[s - cal(1) - cal(2) - cal(4)];
if(cal(1) + cal(4) + cal(3) <= s)res -= dp[s - cal(1) - cal(4) - cal(3)];
if(cal(4) + cal(2) + cal(3) <= s)res -= dp[s - cal(4) - cal(2) - cal(3)];
if(cal(1) + cal(2) + cal(3) + cal(4) <= s)res += dp[s - cal(1) - cal(2) - cal(3) - cal(4)];
printf("%lld\n", res);
}
return 0;
}
打表是一种浪漫
本文来自博客园,作者:correct,转载请注明原文链接:https://www.cnblogs.com/correct/p/12862033.html

浙公网安备 33010602011771号