题目链接: https://www.luogu.com.cn/problem/P1450
乍一看是多重背包的题目,没错就是多重背包,但是复杂度呢?
O
(
1
0
5
?
1
0
5
?
1000
)
O(10^5 * 10 ^5 * 1000)
O(105?105?1000)太大,必然超时
本题主要使用容斥
不明白容斥的话可以先去学一下容斥的思想及原理
用没有条件限制(物品数量限制)的答案数减去不满足限制(选择的物品数量超过了题目中的限制)的答案数就是要求(题目限制物品数量)的答案数
f
[
j
]
f[j]
f[j]代表没有题目中的物品数量限制后的答案情况数(用完全背包去求)
本题使用枚举子集的技巧,本题可以用枚举
1
?
15
1-15
1?15来代替
枚举子集的技巧可以先去百度学一下
for(int i = s; i ; i = (i - 1) & s)
不满足限制的就是取数量大于
d
[
i
]
d[i]
d[i]的,本题取
d
[
i
]
+
1
d[i] + 1
d[i]+1就是不满足限制,情况数就为
f
[
s
?
(
d
[
i
]
+
1
)
?
c
[
i
]
]
f[s - (d[i] + 1) * c[i]]
f[s?(d[i]+1)?c[i]]
物品号未id 不满足数量限制的话,它对应的不满足限制的情况数就为
f
[
s
?
(
d
[
i
d
]
+
1
)
?
c
[
i
d
]
]
f[s - (d[id] + 1) * c[id]]
f[s?(d[id]+1)?c[id]]
容斥操作就是,总情况数 减去单个不满足限制的,加上两两之间不满足限制的,减去三个三个之间不满足限制的…(求集合并的运算)
如何区分不满足限制的物品数量呢?下面代码中x 为1 代表的数量为奇数,为0 的话代表的数量为偶数
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 1e5 + 5;
ll f[N];
ll c[5], d[5], s;
int main()
{
for(int i = 1; i <= 4; i++)
cin >> c[i];
f[0] = 1;
for(int i = 1; i <= 4; i++)
for(int j = c[i]; j < N; j++)
f[j] += f[j - c[i]];
int t;
cin >> t;
while(t--)
{
cin >> d[1] >> d[2] >> d[3] >> d[4] >> s;
ll res = f[s];
int ss = 15;
for(int i = ss; i ; i = (i - 1) & ss)
{
ll x = 0, tmp = 0;
for(int j = 0 ; j < 4; j++)
{
if(i >> j & 1)
x ^= 1, tmp += c[j + 1] * (d[j + 1] + 1);
}
if(s >= tmp)
res = x ? res - f[s - tmp] : res + f[s - tmp];
}
cout << res << "\n";
}
return 0;
}
|