背包九讲(二)——完全背包问题

背包九讲(二)——完全背包问题

参考了著名的背包九讲,可以在这里下载,绪前一篇的01背包问题

1.1 问题

N种物品和一个容量为V的背包,每种物品都有无限件可用,放入第i件物品的费用是 C i C_i Ci,得到的价值是 W i W_i Wi。求解将哪些物品装入背包可使价值综合最大。

1.2 解题思路

完全背包和01背包唯一的区别就在于一件物品是否能多次选择?

知道这个区别,其实仔细一想,就能完全背包问题转化为01背包,因为容量V是有限的,对于单个物品 C i C_i Ci来说,最多只能放如 ⌊ V / C i ⌋ \left \lfloor V/C_i\right \rfloor V/Ci件,所以我们只需要将完全背包问题看成是有 ⌊ V / C 1 ⌋ \left \lfloor V/C_1\right \rfloor V/C1件物品 C 1 C_1 C1、有 ⌊ V / C 2 ⌋ \left \lfloor V/C_2\right \rfloor V/C2件物品 C 2 C_2 C2、…、 ⌊ V / C N ⌋ \left \lfloor V/C_N\right \rfloor V/CN件物品 C N C_N CN的01背包问题就可以了。但是此时总的复杂度可以认为是 O ( N V ∑ V C i ) O(NV\sum \cfrac{V}{C_i}) O(NVCiV),这个复杂度有点高了~

更进一步,我们其实有更加高效的转化方法,用二进制的思想,我们可以将第 i 种物品拆解为费用为 C i 2 K C_i2^K Ci2K,价值为 W i 2 k W_i2^k Wi2k的若干件物品,其中k取1至 C i 2 k ≤ V C_i2^k \le V Ci2kV的最大整数(其实k可以取所有i种物品费用和小于V的整数),然后时间复杂度就可以优化为 O ( N V l o g ( ⌊ V / C 2 ⌋ ) ) O(NVlog(\left \lfloor V/C_2\right \rfloor)) O(NVlog(V/C2))了。相比之前低了不少~

当然,最nb的解法都不是上面说的hhh,在求解01背包问题时需要要求 v v v 按照递减的顺序从 V , V − 1 , . . , 0 V,V-1,..,0 V,V1,..,0 计算 d p [ v ] dp[v] dp[v] ,让 v 递减计算是为了保证状态 d p [ v ] dp[v] dp[v]是由上一轮递推而来的,其实也就是保证一件物品只被选择一次,但是现在允许选择多次了,岂不美哉~ 非常简单,让 v 递增计算dp[v]即可,请参考下面伪代码:

dp[0...V]0
for i ← 1 to N  // 依次遍历N件物品
	for v ← Ci to V // 能装下当前物品的背包使用状态方程
		dp[v] ← max{
    
    dp[v], dp[v-Ci]+Wi}

所以此时问题的时间复杂度已经降到了 O ( N V ) O(NV) O(NV)了,秒啊~

但是完全背包就不存在01背包里的那个常数优化了~

一个简单有效的优化,若两件物品 i,j 满足 C i < C j C_i \lt C_j Ci<Cj并且 W i ≥ W j W_i \ge W_j WiWj,那么可以直接将物品 j 直接去掉,这个过程可以在 O ( N 2 ) O(N^2) O(N2)的时间复杂度内实现,并不是很高,如果被卡时间了,可以试试这个简单的优化~

1.3 示例代码

懒得写了~还得自己造数据,和01背包问题代码类似的

1.4 实战阶段

做几道题就清楚辣!!

扫描二维码关注公众号,回复: 11993750 查看本文章

洛谷P1616 疯狂的采药

题目描述

LiYuxiang 是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同种类的草药,采每一种都需要一些时间,每一种也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。”

如果你是 LiYuxiang,你能完成这个任务吗?

此题和原题的不同点:

  1. 每种草药可以无限制地疯狂采摘。

  2. 药的种类眼花缭乱,采药时间好长好长啊!师傅等得菊花都谢了!

具体输入输出格式以及样例请参考原题网站~


这就是一个典型的完全背包问题,直接按照上面的思路写就可以,但是这题的数据量是比较大的,注意dp数组使用long long类型,以及动态生成数组防止内存爆炸,还有就是STL是在太慢了用malloc吧

#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
int main() {
    
    
	ios::sync_with_stdio(false), cin.tie(NULL);
	// 静态数组会爆内存
	//int V[N] = { 0 }, C[N] = { 0 }, t, m;
	//long long dp[N] = { 0 };

	int t, m;
	cin >> t >> m;
	int* V = (int*)malloc(m * sizeof(int));
	int* C = (int*)malloc(m * sizeof(int));
	long long* dp = (long long*)malloc((t+1) * sizeof(long long));
	// STL太慢了
	//vector<int> V(m);
	//vector<int> C(m);
	//vector<long long> dp(t+1);
	for (int i = 0; i < m; i++) {
    
    
		cin >> C[i] >> V[i];
	}
	for (int i = 0; i < m; i++) {
    
    
		for (int j = C[i]; j <= t; j++) {
    
    
			dp[j] = max(dp[j], dp[j - C[i]] + V[i]);
		}
	}
	cout << dp[t] << endl;
	free(V);
	free(C);
	free(dp);
	return 0;
}

提交结果:

07Kye1.png

如果使用STL。。。。

07KWWD.png

STL是malloc的两倍了~当然这里不卡时间没有啥影响

猜你喜欢

转载自blog.csdn.net/weixin_44338712/article/details/109105987