动态规划:背包问题

简介: 背包问题

 

题目描述:

 

n 个物品和一个大小为 m 的背包. 给定数组 A 表示每个物品的大小和数组 V 表示每个物品的价值.问最多能装入背包的总价值是多大?

    1. A[i], V[i], n, m 均为整数
    2. 你不能将物品进行切分
    3. 你所挑选的要装入背包的物品的总大小不能超过 m
    4. 每个物品只能取一次
    5. m <= 1000m<=1000\

    len(A),len(V)<=100len(A),len(V)<=100

    思路分析:

    对于每一个物品,它有四种情况:

    ①价值大,体积大。②价值大,体积小。③价值小,体积大。④价值小,体积小。

    因此,在一个背包中,在装入物品的时候,我们需要考虑一种特殊情况和两种常见情况。

    特殊情况:装不下。常见情况:放和不放。

    我们先定义问题需要的状态:

    F(i,j):表示从第i个商品中选择了商品后,大小为j的背包的价值。

    状态转移方程:

    图中,F中的i是从1开始的,A和V中的i和j是从0开始的。

    ]%N{ZAFLF0H7_8(GC9OU88V.png

    特殊情况:如果装不下,那么此时的价值和前i-1个情况的价值是一样的,即F(i,j) = F(i-1,j);

    如果可以装入:需要在两种选择中找最大的,即F(i, j) = max{F(i-1,j), F(i-1, j - A[i]) + V[i]}。

    其中,F(i-1,j): 表示不把第i个物品放入背包中, 所以它的价值就是前i-1个物品放入大小为j的背包的最大价值。F(i-1, j - A[i]) + V[i]:表示把第i个物品放入背包中,价值增加V[i],但是需要腾出j - A[i]的大小放第i个商品。

    初始化:第0行和第0列都为0,表示没有装物品时的价值都为0,即F(0,j) = F(i,0) = 0;

    A9BMF18MS%0R7W0FJ7OH3WR.png

    返回值:F(n,m);

    代码如下:

    int backPackII(int m, vector<int> &a, vector<int> &v) {
            // write your code here
            if(a.empty() || v.empty() || m< 1)
            {
                return 0;
            }
            //多加一行一列,用于设置初始条件
            int N = a.size()+1;
            int M = m+1;
            vector<vector<int>> ret(N,vector<int>(M,0));
            for(int i = 1;i<N;++i)
            {
                for(int j = 1;j<M;++j)
                {
                    //如果物品的体积大于j,说明放不下了
                    //那就跟i-1的价值意义
                    if(a[i-1]>j)
                    {
                        ret[i][j] = ret[i-1][j];
                    }
                    else //装得下,放或不放
                    {
                        //如果不放,那价值也跟i-1的价值一样
                        //如果放,需要腾出放物品i的空间
                        ret[i][j] = max(ret[i-1][j],ret[i-1][j-a[i-1]]+v[i-1]);
                    }
                }
            }
            return ret[N-1][m];
        }

    image.gif

    优化:

    上面的算法在计算第i行元素时,只用到第i-1行的元素,所以二维的空间可以优化为一维空间。但是如果是一维向量,需要从后向前计算,因为后面的元素更新需要依靠前面的元素未更新(模拟二维矩阵的上一行的值)的值。

    int backPackII(int m, vector<int> A, vector<int> V) {
      if (A.empty() || V.empty() || m < 1) 
      {
        return 0;
      } 
      const int N = A.size();
      //多加一列,用于设置初始条件,因为第一件商品要用到前面的初始值
      const int M = m + 1;
      vector<int> result;
      //初始化所有位置为0,第一行都为0,初始条件
      result.resize(M, 0);
      //这里商品的索引位置不需要偏移,要和未优化的方法区分开
    //这里的i-1理解为上一行,或者未更新的一维数组值
      for (int i = 0; i != N; ++i)
      {
        for (int j = M - 1; j > 0; --j) 
        {
          //如果第i个商品大于j,说明放不下, 所以(i,j)的最大价值和(i-1,j)相同
          if (A[i] > j) 
          {
            result[j] = result[j];
          } //如果可以装下,分两种情况,装或者不装
            //如果不装,则即为(i-1, j)
            //如果装,需要腾出放第i个物品大小的空间: j - A[i]
            // 装入之后的最大价值即为(i - 1, j -A[i - 1]) + 第i个商品的价值V[i]
            //最后在装与不装中选出最大的价值
          else 
          {
            int newValue = result[j - A[i]] + V[i];
            result[j] = max(newValue, result[j]);
          }
        }
      } //返回装入前N个商品,物品大小为m的最大价值
        return result[m];
    };

    image.gif

    相关文章
    |
    Java 关系型数据库 MySQL
    学生公寓报修管理系统的设计与实现(论文+源码)_kaic
    学生公寓报修管理系统的设计与实现(论文+源码)_kaic
    |
    Java API Android开发
    Android native应用开发简明教程 (1) - 本地开发武器库概览
    native应用比起Java应用来,跟Android版本的相关性更高一些。 所以,这些API都是根据平台版本号分成不同的目录的。 我们来看看Android为我们提供了哪些API
    6619 0
    |
    10月前
    |
    开发者 Python
    如何在Python中管理模块和包的依赖关系?
    在实际开发中,通常会结合多种方法来管理模块和包的依赖关系,以确保项目的顺利进行和可维护性。同时,要及时更新和解决依赖冲突等问题,以保证代码的稳定性和可靠性
    307 62
    |
    12月前
    |
    机器学习/深度学习 人工智能 自然语言处理
    深度学习中的卷积神经网络(CNN)及其应用
    【9月更文挑战第24天】本文将深入探讨深度学习中的一种重要模型——卷积神经网络(CNN)。我们将通过简单的代码示例,了解CNN的工作原理和应用场景。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的信息。
    296 1
    |
    SQL 运维 关系型数据库
    MySQL数据库运维第一篇(日志与主从复制)
    MySQL数据库运维第一篇(日志与主从复制)
    |
    10月前
    |
    网络协议 安全 网络安全
    OSPF的安全性考虑:全面解析与最佳实践
    OSPF的安全性考虑:全面解析与最佳实践
    297 0
    |
    机器学习/深度学习 自然语言处理 计算机视觉
    深度学习之文本引导的图像编辑
    基于深度学习的文本引导的图像编辑(Text-Guided Image Editing)是一种通过自然语言文本指令对图像进行编辑或修改的技术。
    230 9
    |
    缓存 安全 Unix
    深入探索Linux中的qemu-ga命令
    **QEMU的qemu-ga是虚拟机内的守护进程,提供带外通道管理guest OS,如文件操作、关机、休眠等。它通过virtio-serial通信,特点是安全、高效、灵活。例如,使用`virsh qemu-agent-command`执行虚拟机内部命令。最佳实践包括安装配置agent、设置黑名单、考虑安全和性能、定期备份及利用社区资源。**
    |
    11月前
    |
    机器学习/深度学习 自然语言处理 算法
    神经网络算法以及应用场景和基本语法
    神经网络算法以及应用场景和基本语法
    244 0