容斥定理背包问题P1450 [HAOI2008]硬币购物
Posted 行码棋
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了容斥定理背包问题P1450 [HAOI2008]硬币购物相关的知识,希望对你有一定的参考价值。
题目链接:
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)
// i代表二进制表示的就是子集
不满足限制的就是取数量大于 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;
以上是关于容斥定理背包问题P1450 [HAOI2008]硬币购物的主要内容,如果未能解决你的问题,请参考以下文章
[Luogu P1450] [HAOI2008]硬币购物 背包DP+容斥