Time Limit: 10 Sec Memory Limit: 162 MB
Description
硬币购物一共有4种硬币。面值分别为c1,c2,c3,c4。某人去商店买东西,去了tot次。每次带di枚ci硬币,买si的价值的东西。请问每次有多少种付款方法。
Input
第一行 c1,c2,c3,c4,tot 下面tot行 d1,d2,d3,d4,s,其中di,s<=100000,tot<=1000
Output
每次的方法数
题目分析
第一眼单调队列优化多重背包,但再一看数据范围emmmm
首先假如没有物品个数的限制,那么这就是一个完全背包,这样的情况可以直接预处理
现在考虑加入限制,当前
d
p
[
S
i
]
dp[S_i]
dp[Si]中包含的超过
d
i
d_i
di限制的方案数为
d
p
[
S
i
−
c
i
∗
(
d
i
+
1
)
]
dp[S_i-c_i*(d_i+1)]
dp[Si−ci∗(di+1)]
相当于先强制往包中放入
d
i
+
1
d_i+1
di+1个
c
i
c_i
ci,剩余
S
i
−
c
i
∗
(
d
i
+
1
)
S_i-c_i*(d_i+1)
Si−ci∗(di+1)的容积不管怎么放都已经是不合法的了
先令
a
n
s
=
d
p
[
S
i
]
ans=dp[S_i]
ans=dp[Si],然后
a
n
s
−
=
∑
i
=
1
4
d
p
[
S
i
−
c
i
(
d
i
+
1
)
]
ans-=\sum_{i=1}^4dp[S_i-c_i(d_i+1)]
ans−=∑i=14dp[Si−ci(di+1)]
但是这时候我们又发现
c
i
,
c
j
(
i
!
=
j
)
c_i,c_j(i!=j)
ci,cj(i!=j)同时不合法的方案数多减了,我们再加回来
这时叒发现
c
i
,
c
j
,
c
k
(
i
!
=
j
!
=
k
)
c_i,c_j,c_k(i!=j!=k)
ci,cj,ck(i!=j!=k)同时不合法的方案数多加了,我们再减掉
以及叕加回四个同时不合法的方案数
所以就是个容斥问题
一般容斥都可以转化成二进制简化运算
复杂度约为
O
(
4
∗
m
a
x
n
+
T
∗
2
4
)
O(4*maxn+T*2^4)
O(4∗maxn+T∗24)
#include<iostream>
#include<vector>
#include<algorithm>
#include<queue>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
typedef long long lt;
int read()
{
int f=1,x=0;
char ss=getchar();
while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
return f*x;
}
const int maxn=100010;
int T,n;
int ci[5],di[5];
lt dp[maxn],S;
int main()
{
for(int i=1;i<=4;++i) ci[i]=read();
dp[0]=1;
for(int i=1;i<=4;++i)
for(int j=ci[i];j<maxn;++j)
dp[j]+=dp[j-ci[i]];
T=read();
while(T--)
{
for(int i=1;i<=4;++i) di[i]=read();
S=read(); lt res=dp[S];
for(int i=1;i<=(1<<4)-1;++i)
{
int tt=S,cnt=0;
for(int j=1;j<=4;++j)
if(i&(1<<j-1)) cnt++,tt-=ci[j]*(di[j]+1);
if(tt<0) continue;
if(cnt&1) res-=dp[tt];
else res+=dp[tt];
}
printf("%lld\n",res);
}
return 0;
}