容斥定理背包问题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(1051051000)太大,必然超时

本题主要使用容斥

不明白容斥的话可以先去学一下容斥的思想及原理

用没有条件限制(物品数量限制)的答案数减去不满足限制(选择的物品数量超过了题目中的限制)的答案数就是要求(题目限制物品数量)的答案数

f [ j ] f[j] f[j]代表没有题目中的物品数量限制后的答案情况数(用完全背包去求)

本题使用枚举子集的技巧,本题可以用枚举 1 − 15 1-15 115来代替

枚举子集的技巧可以先去百度学一下

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]]

容斥操作就是,总情况数 减去单个不满足限制的,加上两两之间不满足限制的,减去三个三个之间不满足限制的…(求集合并的运算)

如何区分不满足限制的物品数量呢?下面代码中x1代表的数量为奇数,为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]硬币购物的主要内容,如果未能解决你的问题,请参考以下文章

容斥定理背包问题P1450 [HAOI2008]硬币购物

P1450 [HAOI2008]硬币购物(完全背包+容斥)

[Luogu P1450] [HAOI2008]硬币购物 背包DP+容斥

P1450 [HAOI2008]硬币购物

洛谷P1450 [HAOI2008]硬币购物 动态规划 + 容斥原理

题解 P1450 [HAOI2008]硬币购物