动态规划背包问题——多重背包(附带混合背包)

多重背包:

问题背景:
有一个容量为V的背包,有N种物品,每一种物品的体积为c[i],对应的价值为v[i],数量为s[i],选择最佳方案,使背包中所装的物品的总价值最大。
有了之前的基础,我们就能轻松地写出状态转移方程
f[i][j] = max{ f[i-1][j – k * c[i]] + k * c[i] | k * c[i] <=j, k <= s[i]}
时间复杂度O(V*Σs[i])
先给出这样的代码:

for(int i = 1; i <= N; i++)  // N为物品种类 V为背包容量 
	for(int j = V; j >= c[i]; j--)
 	// c表示物品体积  s为物品数量 v表示物品的价值 
		for(int k = 1; k <= s[i]; k++)
		if(j >= k * s[i])
		f[j] = max(f[j], f[j - k * c[i]] + k * v[i])
		cout << f[V];

这个很好理解,先问你第一个物品i拿还是不拿,再问你第二个物品i拿还是不拿,一直问到没有物品i或者装不下了。但是这样的复杂度太大。
我们可以考虑使用二进制的思想,优化为O(V*Σlog s[i]) (这个思想其实在完全背包中也能使用,但是复杂度是O(V Σlog V/c[i]),并没有O(VN)算法好,所以在完全背包那一讲没有提到,放到多重背包来讲
什么是二进制思想呢?
将第i个物品分成若干个物品,给每一个物品分配一个系数k,就相当于把这k个物品看做是一个物品,同样,这个物品的价值也是k倍,让这些系数为1,2,4,…,2(K-1),s[i]-2k-1,保证这些系数都大于零,k是满足最后一个式子大于0的最大整数。
比如 13 = 1 + 2 + 4 + 6
对于一种有13个的物品,如果你想拿7个 就拿1+6如果想拿3个就拿1+2 这样一来,我们就不用判断13次了,大大减少了复杂度。这样将物品分组之后,我们只需要对他们都进行01背包就可以了。
最后贴上代码:

for(int i = 1; i <= N; i++)
// N 为种类数 V为背包容量 c为物品体积 v为物品价值 s为物品数量 
	{
		for(int k = 1; k <= s[i]; k *= 2)//二进制思想在这里体现 
		{
			for(int j = V; j >= c[i] * k; j--)
			f[j] = max(f[j], f[j - c[i] * k] + v[i] * k);
			s[i] -= k;
		}
		for(int j = V; j >= c[i] * s[i]; j--)
		//这里是 01背包 V >= c[i] * s[i]就是表示第i件物品都能装下去 
			f[j] = max(f[j], f[j - c[i] * s[i]] + s[i] * v[i]);
	}
		
		cout << f[V] << endl;

在这里顺便提一下,多重背包问题也是有O(VN)算法的,是使用单调队列的方法,这里就不介绍了,感兴趣的可以自行搜索。

混合背包问题

问题背景
将01背包,完全背包,多重背包混合起来,即有的物品只能取一次,有的物品可以取无限次,有的物品能取一定的次数。这样我们如何求解呢。
其实很简单,我这这里给出伪代码:

**for 1=1…N
		if 第i件物品属于01背包
			zeroonepack(c[i], v[i])
		else if 第i件物品属于完全背包
			completepack(c[i], v[i])
		else if 第i件物品属于多重背包
			multiplepack(c[i], v[i], s[i])**

复杂的问题是由简单问题组合而来,当我初次看到混合背包问题的时候我也感觉这个问题好难,但经过分析之后,发现无非就是前面三种基础背包的组合。

注:此文章问原创,开源,可转载但请注明出处!

猜你喜欢

转载自blog.csdn.net/yezi_coder/article/details/103501760