博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
一文读懂背包问题
阅读量:4046 次
发布时间:2019-05-25

本文共 2344 字,大约阅读时间需要 7 分钟。

转自:刘毅

https://www.61mon.com/index.php/archives/188/


问题展开


有 N 件物品和一个容量为 V 的背包。第 i 件物品的体积是 Ci,其价值是 Wi。求解,在不超过背包容量情况下,将哪些物品装入背包可使价值总和最大。


基本思路


这是最基础的背包问题,特点是:每种物品仅有一件。


状态 F[i,v] 表示前 i 件物品中选择若干件放在容量为 v 的背包中,可以取得的最大价值。


转移方程:



对于第 i 件物品,有放与不放两种选择。若选择不放,F[i,v]=F[i−1,v];若选择放,v−Ci 确保有足够的空间,随之 F[i,v]=F[i−1,v−Ci]+Wi。


代码展开


/**

 *

 * author 刘毅(Limer)

 * date   2017-03-17

 * mode   C++

 */

#include <iostream>

#include <algorithm>

using namespace std;

int main()

{

    const int N = 6;                     // 物品个数

    const int V = 10;                    // 背包体积

     // 第 i 个物品的体积(下标从 1 开始)

    int C[N + 1] = { -1,5,6,5,1,19,7 }; 

     // 第 i 个物品的价值

    int W[N + 1] = { -1,2,3,1,4,6,5 };   

     // 状态

    int F[N + 1][V + 1] = { 0 };        

    // 对于第 i 个物品

    for (int i = 1; i <= N; i++) 

        for (int v = 0; v <= V; v++)

        {

            F[i][v] = F[i - 1][v];  //第 i 个不放

            // 如果比它大,再放第 i 个

            if (v - C[i] >= 0 && F[i][v] < F[i - 1][v - C[i]] + W[i])                 

                   F[i][v] = F[i - 1][v - C[i]] + W[i];

        }

    cout<< F[N][V] << endl;  

    return 0;

}


以上方法的时间和空间复杂度均为 O(VN),其中时间复杂度应该已经不能再优化了,但空间复杂度却可以优化到 O(V)。


先考虑上面讲的基本思路如何实现,肯定是有一个主循环i ← 1 to N,每次算出来二维数组 F[i,v] 的所有值。


那么,如果只用一个数组 F[v] 能不能保证第 i 次循环结束后 F[v] 中表示的就是我们定义的状态 F[i,v] 呢?


F[i,v] 是由 F[i−1,v] 和 F[i−1,v−Ci] 两个子问题递推而来,能否保证在推 F[i,v] 时(也即在第 i 次主循环中推 F[v] 时)能够取用 F[i−1,v] 和 F[i−1,v−Ci] 的值呢?


事实上,这要求在每次主循环中我们以v ← V to C[i]的递减顺序计算 F[v],这样才能保证计算 F[v] 时 F[v−Ci] 保存的是状态 F[i−1,v−Ci] 的值。


优化后的代码如下:


/**

 *

 * author 刘毅(Limer)

 * date   2017-03-17

 * mode   C++

 */

#include <iostream>

#include <algorithm>

using namespace std;

int main()

{

    const int N = 6;                     // 物品个数

    const int V = 10;                    // 背包体积

    // 第 i 个物品的体积(下标从 1 开始)

    int C[N + 1] = { -1,5,6,5,1,19,7 };  

    // 第 i 个物品的价值

    int W[N + 1] = { -1,2,3,1,4,6,5 };  

    int F[V + 1] = { 0 };                // 状态

    for (int i = 1; i <= N; i++)  // 对于第 i 个物品

        for (int v = V; v >= C[i]; v--)

            F[v] = max(F[v], F[v - C[i]] + W[i]);

    cout << "最大价值是:" << F[V] << endl;  

    return 0;

}


初始化的细节问题


我们看到的求最优解的背包问题题目中,事实上有两种不太相同的问法。有的题目要求 “恰好装满背包” 时的最优解,有的题目则并没有要求必须把背包装满。


这两种问法的实现方法只是在初始化的时候有所不同。


如果是第一种问法,要求恰好装满背包,那么在初始化时除了 F[0] 为 0,其它 F[1]...F[V] 均设为−∞,这样就可以保证最终得到的 F[V] 是一种恰好装满背包的最优解。


如果并没有要求必须把背包装满,而是只希望价格尽量大,初始化时应该将 F[0]...F[V] 全部设为 0。


这是为什么呢?可以这样理解:初始化的 F 数组事实上就是在没有任何物品可以放入背包时的合法状态。


如果要求背包恰好装满,那么此时只有容量为 0 的背包可以在什么也不装且价值为 0 的情况下被 “恰好装满”,其它容量的背包均没有合法的解,属于未定义的状态,应该被赋值为 -∞了。


如果背包并非必须被装满,那么任何容量的背包都有一个合法解 “什么都不装”,这个解的价值为 0,所以初始时状态的值也就全部为 0 了。


参考文献


背包九讲。

分享朋友圈 也是另一种赞赏

The more we share, The more we have

 

欢迎加入数据君高效数据分析社区


加我私人微信进入大数据干货群:tongyuannow 






目前100000+人已关注加入我们

       

       




转载地址:http://nrzci.baihongyu.com/

你可能感兴趣的文章
GBK编码下jQuery Ajax中文乱码终极暴力解决方案
查看>>
Oracle 物化视图
查看>>
PHP那点小事--三元运算符
查看>>
解决国内NPM安装依赖速度慢问题
查看>>
Brackets安装及常用插件安装
查看>>
Centos 7(Linux)环境下安装PHP(编译添加)相应动态扩展模块so(以openssl.so为例)
查看>>
fastcgi_param 详解
查看>>
Nginx配置文件(nginx.conf)配置详解
查看>>
标记一下
查看>>
IP报文格式学习笔记
查看>>
autohotkey快捷键显示隐藏文件和文件扩展名
查看>>
Linux中的进程
查看>>
学习python(1)——环境与常识
查看>>
学习设计模式(3)——单例模式和类的成员函数中的静态变量的作用域
查看>>
自然计算时间复杂度杂谈
查看>>
当前主要目标和工作
查看>>
使用 Springboot 对 Kettle 进行调度开发
查看>>
一文看清HBase的使用场景
查看>>
解析zookeeper的工作流程
查看>>
搞定Java面试中的数据结构问题
查看>>