其他分享
首页 > 其他分享> > 背包问题初步

背包问题初步

作者:互联网

背包问题初步

  背包问题(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