题目大意:给出4个硬币的价值和个数限制,求有多少种方法凑成S块钱。
思路:很巧妙的一种想法,用到了4这个非常小的数字。我们可以先不管每个硬币的个数限制,然后跑一次完全背包。之后把不符合的情况去除就行了。方法是,先减去一种硬币超限的数目,然后加上两种硬币超限的数目,然后减去三种硬币超限的数目,然后加上四种硬币超限的个数。当然代码就很丑了。。
CODE:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAX 100010
#define P(i) (c[i] * (l[i] + 1))
using namespace std;
int c[5],l[5],s;
int cnt;
long long f[MAX],ans;
int main()
{
for(int i = 1; i <= 4; ++i)
scanf("%d",&c[i]);
cin >> cnt;
for(int i = 1; i <= cnt; ++i) {
for(int j = 1; j <= 4; ++j)
scanf("%d",&l[j]);
scanf("%d",&s);
memset(f,0,sizeof(f));
f[0] = 1;
for(int _i = 1; _i <= 4; ++_i)
for(int j = c[_i]; j <= s; ++j)
f[j] += f[j - c[_i]];
ans = f[s];
for(int _i = 1; _i <= 4; ++_i)
if(s - P(_i) >= 0)
ans -= f[s - P(_i)];
if(s - P(1) - P(2) >= 0) ans += f[s - P(1) - P(2)];
if(s - P(2) - P(3) >= 0) ans += f[s - P(2) - P(3)];
if(s - P(3) - P(4) >= 0) ans += f[s - P(3) - P(4)];
if(s - P(2) - P(4) >= 0) ans += f[s - P(2) - P(4)];
if(s - P(1) - P(3) >= 0) ans += f[s - P(1) - P(3)];
if(s - P(1) - P(4) >= 0) ans += f[s - P(1) - P(4)];
if(s - P(1) - P(2) - P(3) >= 0) ans -= f[s - P(1) - P(2) - P(3)];
if(s - P(2) - P(3) - P(4) >= 0) ans -= f[s - P(2) - P(3) - P(4)];
if(s - P(1) - P(3) - P(4) >= 0) ans -= f[s - P(1) - P(3) - P(4)];
if(s - P(1) - P(2) - P(4) >= 0) ans -= f[s - P(1) - P(2) - P(4)];
if(s - P(1) - P(2) - P(3) - P(4) >= 0) ans += f[s - P(1) - P(2) - P(3) - P(4)];
printf("%lld\n",ans);
}
return 0;
}