题目地址:http://www.lydsy.com/JudgeOnline/problem.php?id=1042
题目大意:4种硬币,面值分别为c1,c2,c3,c4,每种硬币分别有d1,d2,d3,d4枚,买价值为s的东西,有几种付款方法。
算法讨论:
由于做多重背包会超时,考虑其他算法。
首先做完全背包,设f[i]表示面值为i的方案数,转移方程为f[i]=sigma(f[i-cj])。
然后对4种硬币进行容斥,强制某些硬币超过限制,取di+1枚,对剩余的硬币进行分配。
Code:
#include <cstdio>
#define N 100000
using namespace std;
bool v[5];
int T,s,c[5],d[5];
long long ans,f[N+10];
inline void dp(){
f[0]=1;
for (int i=1;i<=4;++i)
for (int j=0;j<=100000-c[i];++j) f[j+c[i]]+=f[j];
}
void dfs(int k,int cnt){
if (k>4){
long long t=s;
for (int i=1;i<=4;++i)
if (v[i]) t-=(long long)(d[i]+1)*c[i];
if (t>=0) if (cnt&1) ans-=f[t];else ans+=f[t];
return;
}
v[k]=0;
dfs(k+1,cnt);
v[k]=1;
dfs(k+1,cnt+1);
}
int main(){
#ifndef ONLINE_JUDGE
freopen("1042.in","r",stdin);
freopen("1042.out","w",stdout);
#endif
for (int i=1;i<=4;++i) scanf("%d",&c[i]);
scanf("%d",&T);
dp();
while (T--){
for (int i=1;i<=4;++i) scanf("%d",&d[i]);
scanf("%d",&s);
ans=0;
dfs(1,0);
printf("%lld\n",ans);
}
return 0;
}