背包问题初步
作者:互联网
背包问题初步
背包问题(Knapsack problem)是一种组合优化的NP完全问题。问题可以描述为:给定一组物品,每种物品都有自己的重量和价格,在限定的总重量内,我们如何选择,才能使得物品的总价格最高。问题的名称来源于如何选择最合适的物品放置于给定背包中。相似问题经常出现在商业、组合数学,计算复杂性理论、密码学和应用数学等领域中。也可以将背包问题描述为决定性问题,即在总重量不超过W的前提下,总价值是否能达到V?它是在1978年由Merkle和Hellman提出的。
————————引用自百度百科“背包问题”
01背包问题
问题描述:
有n个重量和价值分别为wi,vi的物品,从这些物品中挑选出总重不超过m的若干物品,使挑选出的物品价值最高。
思路:
如果使用暴力枚举的思想,01背包问题的时间复杂度是非常高的,并不适合在算法竞赛中使用。
而贪心的思想也并不适用,举个例子,对于数据
n = 3, m = 4 w = {3, 2, 2} v = {5, 3, 3}
如果优先选择性价比(即价值 / 重量)最高的物品,那么背包在装入1号物品后便无法再装入剩余物品,此时背包总价值为5,然而实际上的最优方案是选择2,3号物品,总价值为6。
对于01背包问题,通常采用动态规划的方案。
1 for (int i = 1; i <= n;i++) 2 for (int j = 0; j <= m;j++) 3 { 4 if(j>=w[i]) 5 f[i][j] = max(f[i - 1][j], f[i - 1][j - w[i]] + v[i]); 6 else 7 f[i][j] = f[i - 1][j]; 8 }
定义数组 f(i,j) 表示对于前 i 个物品,背包容量为 j 时可以得到的最大容量
状态转移方程为
f[i][j] = max(f[i - 1][j], f[i - 1][j - w[i]] + v[i]);
简单的解释就是,对于第 i 件物品,如果拿上它后背包的大小比不拿上它要大,就拿上它
以上写法的空间复杂度还可以进行优化
for (int i = 1; i <= n; i++) { for (int j = m; j >=w[i]; j--) f[j] = max(f[j],f[j - w[i]] + v[i]); }
只需要一维数组,j 从后往前遍历,j 右侧相当于前一种写法中的 f(i, j) 左侧相当于 f(i - 1, j) 只需要一维数组便可以存储最终结果,实现了对空间复杂度的优化。
完全背包问题
问题描述:
有n种重量和价值分别为wi,vi的物品,从这些物品中挑选出总重不超过m的若干物品,每种物品的数量不限,使挑选出的物品价值最高。
思路:
完全背包可以看作01背包的一个变种,区别在于01背包每种物品只有一个,而完全背包问题中每种物品有无限个
所以只需要稍微修改01背包的程序
for (int i = 1; i <= n; i++) { for (int j = w[i]; j <=m; j--) f[j] = max(f[j],f[j - w[i]] + v[i]); }
对于第 i 种物品,只要背包仍有足够空间,就可以尽可能的尝试往里放,因此 j 的循环可以从 w[i] 一直到 最大空间 m 为止。
多重背包问题
问题描述:
有n种重量和价值分别为wi,vi的物品,每种物品的数量为ki ,从这些物品中挑选出总重不超过m的若干物品,使挑选出的物品价值最高。
思路:
对于多重背包问题,如果把每种物品看作 k 个不同的物品,那么这个问题就可以转化为普通的01背包问题。但是这样做并不是最优的做法
常用的划分方式,可以将每种物品划分为数种物品,其中每种物品的重量和价值等于 2^0, 2^1, 2^2……2^(n-1) , k - 2^(n-1 )倍的 w 和 v 。这样的划分方式可以使得划分后物品的总价值和总重量不变,同时也可以通过组合的方式实现1——k任意一种数量的选择。
1 int cnt = 0; 2 3 for (int i = 0; i < m; i++) 4 { 5 int a, b, s; 6 cin >> a >> b >> s; 7 int t = 1; 8 while (s - t > 0) 9 { 10 s -= t; 11 c[++cnt] = a * t; 12 w[cnt] = b * t; 13 t <<= 1; 14 } 15 c[++cnt] = a * s; 16 w[cnt] = b * s; 17 }
实现划分之后,可将问题作为普通的背包问题求解。
标签:问题,每种,背包,int,初步,01,物品 来源: https://www.cnblogs.com/iceyz/p/14364426.html