文章目录

  • 1 三种背包问题详解
  • 2 最值问题
    • 1.1 0-1背包问题
    • 1.2 零钱兑换
    • 1.3 一和零
    • 1.4 最后一块石头的重量
  • 3. 恰好背包容量问题
  • 4. 排列组合问题
    • 4.1 目标和
    • 4.2 组合总和Ⅳ

在简单复习完数据结构以后,便开始了算法复习。本博客将结合复习视频与LeetCode题目,面向机考算法复习。背包动态规划问题一般分为三种题型:

  • 最值问题:给定可选物品和限定容量,求最大价值或者最大体积。
    ①0-1背包问题
    ②完全背包问题。
  • 恰好取到背包容量问题
    ①0-1背包问题 + 恰好取到背包容量。
  • 组合问题
    ①考虑顺序得组合问题。
    ②0-1背包组合问题。

1 三种背包问题详解

三种背包问题都建立在0-1背包问题得基础之上的,因此下面我将从0-1背包开始,介绍三种问题的求解方式。
(1)0-1背包问题

  • 最大价值
    dp[i][j]={dp[i−1][j],不能放入max⁡{dp[i−1][j],dp[i−1][j−v[i]]+p[i],可以装入}dp[i][j]=\begin{cases} dp[i-1][j],& 不能放入\\ \max \{dp[i-1][j],dp[i-1][j-v[i]]+p[i], &可以装入\} \end{cases}dp[i][j]={dp[i−1][j],max{dp[i−1][j],dp[i−1][j−v[i]]+p[i],​不能放入可以装入}​
  • 最大体积
    dp[i][j]={dp[i−1][j],不能放入max⁡{dp[i−1][j],dp[i−1][j−v[i]]+v[i],可以装入}dp[i][j]=\begin{cases} dp[i-1][j],& 不能放入\\ \max \{dp[i-1][j],dp[i-1][j-v[i]]+v[i], &可以装入\} \end{cases}dp[i][j]={dp[i−1][j],max{dp[i−1][j],dp[i−1][j−v[i]]+v[i],​不能放入可以装入}​

其中,最大价值和最大体积的区别就是在可以装入时,dp[i][j]加上的是价值还是体积。

(2)完全背包问题
完全背包问题中每个物品可以被无限次的选用,因此相对于0-1背包问题,它的递推方程仅有一处不同:
dp[i][j]={dp[i−1][j],不能放入max⁡{dp[i−1][j],dp[i][j−v[i]]+p[i],可以装入}dp[i][j]=\begin{cases} dp[i-1][j],& 不能放入\\ \max \{dp[i-1][j],dp[i][j-v[i]]+p[i], &可以装入\} \end{cases} dp[i][j]={dp[i−1][j],max{dp[i−1][j],dp[i][j−v[i]]+p[i],​不能放入可以装入}​

即在考虑装入物品i后,还要从物品i开始考虑而不是从物品i-1开始选择。

(3)恰好背包问题
恰好背包问题就是“0-1背包问题+最大体积+判断能否恰好装下”。

(4)组合问题
组合问题是一个物品只能选择一次,输出获得最大价值时物品的选择可能情况。对于物品i在容量为j时的可选择情况是由不装入物品i时的可能情况+装入物品i时的可能情况组成。因此递推方程为:
dp[i][j]={dp[i−1][j],不能放入max⁡{dp[i−1][j],dp[i−1][j−v[i]],可以装入}dp[i][j]=\begin{cases} dp[i-1][j],& 不能放入\\ \max \{dp[i-1][j],dp[i-1][j-v[i]], &可以装入\} \end{cases} dp[i][j]={dp[i−1][j],max{dp[i−1][j],dp[i−1][j−v[i]],​不能放入可以装入}​

(5)排列问题
考虑顺序的组合问题。此时就不能用简单的0-1背包求解了。但可以使用相同的思想,即求第i个物品为末尾的方案数的情况。

for(int i=1; i<=target; i++) {for(int& num:nums) {// 考虑最后一个元素为num的排列个数if(num <=i && dp[i-num] < INT_MAX-dp[i])dp[i] += dp[i-num];}
}

2 最值问题

(1)求解思路

  • 分析是否为背包问题(最值、方案数)
  • 分析是哪一种背包问题。
  • 分析是0-1背包还是完全背包问题。

1.1 0-1背包问题

(1)问题描述
在背包容量有限的情况下,带走总价值最多的物品。
输入

  • 背包容量C
  • n个商品组成集合,每个商品有两个属性viv_ivi​和pip_ipi​,分别表示体积和价格。

约束条件

  • 最大化价值:max⁡∑i∈Spi\max \sum_{i \in S}p_imax∑i∈S​pi​
  • 背包容量限制:∑i∈Svi≤C\sum_{i \in S}v_i \leq C∑i∈S​vi​≤C

(2)思路
假设p(i,c)p(i,c)p(i,c)表示可选前i个商品,容量为C时的最优装填价值。Rec(i,c)Rec(i,c)Rec(i,c)记录第i个物品在背包容量为C时是否装入背包的情况。

  • 选择:对于物品i,它可以选择放入背包或者不放入背包。因此在容量为C,前i个物品可选时的最优价值应当等于上述两种价值中的较大值。
    ①当选择不放入背包时,当前价值应当等于前i-1个物品在背包容量为C时的最优价值,即p(i,c)=p(i−1,c);p(i,c)=p(i-1,c);p(i,c)=p(i−1,c);
    ②当选择放入背包时,当前价值应当等于前i-1个物品容量为C−viC-v_iC−vi​时的最优价值,即p(i,c)=p(i−1,c−vi)+pi;p(i,c)=p(i-1,c-v_i)+p_i;p(i,c)=p(i−1,c−vi​)+pi​;
    p(i,c)=max⁡{p(i−1,c),p(i−1,c−vi)+pi}p(i,c) = \max\{p(i-1,c),p(i-1,c-v_i)+p_i\} p(i,c)=max{p(i−1,c),p(i−1,c−vi​)+pi​}

  • 追踪:通过Rec数组,保存物品i在容量C时的选择情况。当选择装入背包时,Rec(i,c)=1;Rec(i,c)=1;Rec(i,c)=1;否则Rec(i,c)=0;Rec(i,c)=0;Rec(i,c)=0;



(3)核心算法
Knapsack算法

/*
p[n+1], v[n+1]:1~n项分别表示第i个物品的价值和体积
p[0]=v[0]=m[0][0:c]=m[0:n][0]=0
*/
void Knapsack(vector<int>& p, vector<int>& v, int c, int n, vector<vector<int> >& m) {// 从(i=1)-->(i=n)for(int i=0; i<=n; i++) {int jmax = max(c, v[i]-1); // 无法装入for(int j=1; j<=jmax; j++)m[i][j] = m[i-1][j];   // 不装入for(int j=v[i]; j<=c; j++) {m[i][j] = max(m[i-1][j-v[i]]+p[i], m[i-1][j]);                           // 比较选择}}
}

最优解回溯算法

void TraceBack(vector<int>& p, vector<int>& v, vector<vector<int> >& m, vector<int>& x, int c, int n) {for(int i=n; i>0; i--) {if(m[i][c] == m[i-1][c])x[i] = 0;                // 未放入else {x[i] = 1;              // 放入c -= v[i];}}
}

1.2 零钱兑换


问题:完全背包问题 + 最小值问题

  • 本题是完全背包最值问题的变种,求最少的硬币个数。其中每个硬币的价值为它的个数1,因此递推方程如下所示:
    dp[i][j]={dp[i−1][j],不能放入min⁡{dp[i−1][j],dp[i−1][j−v[i]]+1,可以装入}dp[i][j]=\begin{cases} dp[i-1][j],& 不能放入\\ \min \{dp[i-1][j],dp[i-1][j-v[i]]+1, &可以装入\} \end{cases}dp[i][j]={dp[i−1][j],min{dp[i−1][j],dp[i−1][j−v[i]]+1,​不能放入可以装入}​
  • 注意:①因为是最小最值问题,dp第0行初始化为coin_max。
int coinChange(vector<int>& coins, int amount) {int n = coins.size();vector<int> temp(amount+1, 0);vector<vector<int> > m(n+1, temp);for(int j=1; j<=amount; j++)m[0][j] = coin_max;for(int i=1; i<=n; i++) {int jmax = min(amount, coins[i-1]-1);for(int j=1; j<=jmax; j++)              // 总面额不够m[i][j] = m[i-1][j];for(int j=jmax+1; j<=amount; j++) {m[i][j] = min(m[i-1][j], m[i][j-coins[i-1]]+1);     // 完全0-1背包问题}}if(m[n][amount] == coin_max)m[n][amount] = -1;return m[n][amount];
}

1.3 一和零


问题:三维0-1背包问题

  • 目标:最大化子集大小。
  • 约束:选入自己的字符串中的0, 1字符总数不能超过限制。
  • 递推公式如下:
    m[i][j][k]=max⁡{m[i−1][j−zero[i]][k−ones[i]]+1,m[i−1][j][k]}m[i][j][k]=\max\{m[i-1][j-zero[i]][k-ones[i]]+1, m[i-1][j][k] \}m[i][j][k]=max{m[i−1][j−zero[i]][k−ones[i]]+1,m[i−1][j][k]}

注意

  • 无法放入范围的选择:我们通过min(one, ones[i]-1)获得了不能放入当前字符的1的限制范围,同理也能获得0的限制范围。对于三维动态规划问题,可以将限制理解为一个平面。而限制就是所在行的所有列与所在列的所有行。(想象:是需要同时满足两个条件的)
int findMaxForm(vector<string>& strs, int m, int n) {int l = strs.size();vector<int> zeros(l, 0), ones(l, 0);// 初始化ones, zerosfor(int i=0; i<l; i++) {string temp = strs[i];for(int j=0; j<temp.size(); j++) {if(temp[j] == '0')  zeros[i]++;else if(temp[j] == '1') ones[i]++;}}// 三维动态规划,0-1背包问题vector<int> temp1(n+1, 0);vector<vector<int> > temp2(m+1, temp1);vector<vector<vector<int> > > dp(l+1, temp2);for(int i=1; i<=l; i++) {int jmax = min(m, zeros[i-1]-1);int kmax = min(n, ones[i-1]-1);for(int j=0; j<=jmax; j++)                      // 无法放入的for(int k=0; k<=n; k++)dp[i][j][k] = dp[i-1][j][k];for(int k=0; k<=kmax; k++)                      // 无法放入的for(int j=0; j<=m; j++)dp[i][j][k] = dp[i-1][j][k];for(int j=jmax+1; j<=m; j++)                    // 动态规划for(int k=kmax+1; k<=n; k++) {dp[i][j][k] = max(dp[i-1][j][k], dp[i-1][j-zeros[i-1]][k-ones[i-1]]+1);}}return dp[l][m][n];
}

1.4 最后一块石头的重量


问题:0-1背包问题 + 体积最值 + 二分类

  • 对于这一种有加有减的问题,可以将正数和负数分开,当两者趋近于总数的一半时,正数和负数求和最小。
  • 可以将问题理解为,将石头分为减项和被减项,当被减项neg趋近于总和的一半时,两者之差最小。因此问题可以转换为0-1背包求最值问题。
  • 递推方程如下:
    dp[i][j]=max⁡{dp[i−1][j],dp[i−1][j−p[i]]+p[i]}dp[i][j]=\max\{dp[i-1][j], dp[i-1][j-p[i]]+p[i]\}dp[i][j]=max{dp[i−1][j],dp[i−1][j−p[i]]+p[i]}
int lastStoneWeightII(vector<int>& stones) {// 石头均值int c = 0, sum =0;int n = stones.size();for(int i=0; i<stones.size(); i++)sum += stones[i];c = sum/2;vector<int> temp(c+1, 0);vector<vector<int> > dp(n+1, temp);// dpfor(int i=1; i<=n; i++) {int jmax = min(c, stones[i-1]-1);// 无法装入for(int j=1; j<=jmax; j++)dp[i][j] = dp[i-1][j];// 可以装入for(int j=jmax+1; j<=c; j++) {dp[i][j] = max(dp[i-1][j], dp[i-1][j-stones[i-1]]+stones[i-1]);}}return (sum-2*dp[n][c]);
}

3. 恰好背包容量问题


问题:0-1背包 + 体积最值 + 二分类

  • 可以将问题转化为选取一些元素组成第一个子集,使子集的和为总和的一半。因此将问题转化为了0-1背包体积最值问题。
 bool canPartition(vector<int>& nums) {// 1. 求和int sum =0, c =0;int n = nums.size();for(int i=0; i<nums.size(); i++)sum += nums[i];if(sum%2 == 1)              // 奇数不成立return false;c = sum / 2;// 2. 动态规划vector<int> temp(c+1, 0);vector<vector<int>> dp(n+1, temp);for(int i=1; i<=n; i++) {int jmax = min(c, nums[i-1]-1);for(int j=1; j<=jmax; j++)      // 无法装入dp[i][j] = dp[i-1][j];for(int j=jmax+1; j<=c; j++)dp[i][j] = max(dp[i-1][j], dp[i-1][j-nums[i-1]]+nums[i-1]);}// 判断是否能够凑齐cif(dp[n][c] == c)return true;else return false;
}

4. 排列组合问题

4.1 目标和

问题:0-1背包方案问题 + 二分类

  • 给定一个整数数组和target值,求能通过±运算获得target的表达式数目。
  • 因为sum和target值固定,因此减数和被减数唯一确定。所以我们将问题转化为了0-1背包配凑方案问题。

注意:两种无解的情况

  1. target值大于sum
  2. target+sum为奇数,所获得的被减数和减数是分数的情况
int findTargetSumWays(vector<int>& nums, int target) {int sum =0, neg =0;int n = nums.size();for(int i=0; i<n; i++)sum += nums[i];// 对于一个target, 减数和被减数的值是确定的。neg = (sum-target)/2;if(sum <target || (sum-target)%2 == 1)return 0;vector<int> temp(neg+1, 0);vector<vector<int>> dp(n+1, temp);dp[0][0] = 1;for(int i=1; i<=n; i++) {int jmax = min(neg, nums[i-1]-1);for(int j=0; j<=jmax; j++)dp[i][j] = dp[i-1][j];for(int j=jmax+1; j<=neg; j++)dp[i][j] = dp[i-1][j]+dp[i-1][j-nums[i-1]];}return dp[n][neg];
}

4.2 组合总和Ⅳ

问题:排列问题,非0-1背包。采用动态规划求解。

  • 给定正数数组和目标正数,返回总和为target的元素组合数量。不同顺序的序列也属于不同组合。
  • 问题转化为一个排列组合问题,采用动态规划求解。每次考虑以i为结尾的排列序列,从小到大动态求解。

递归方程
dp[i]+=dp[i−num[i]],if(num[i]<=i)dp[i]+=dp[i-num[i]],if(num[i]<=i) dp[i]+=dp[i−num[i]],if(num[i]<=i)

int combinationSum4(vector<int>& nums, int target) {// 排列问题:考虑排列中最后一个元素的个数vector<int> dp(target+1, 0);dp[0] = 1;for(int i=1; i<=target; i++) {for(int& num:nums) {// 考虑最后一个元素为num的排列个数if(num <=i && dp[i-num] < INT_MAX-dp[i])dp[i] += dp[i-num];}}return dp[target];
}

算法分析与设计(一)——0-1背包问题相关推荐

  1. 【算法设计与分析】7、0/1背包问题,动态规划

    /** * 书本:<算法分析与设计> * 功能:给定n种物品和一个背包,物品i的重量是Wi, 其价值为Vi,问如何选择装入背包的物品,使得装入背包的物品的总价值最大? * 文件:beiBa ...

  2. 算法分析与设计——蛮力法0/1背包

    蛮力法0/1背包 蛮力法 蛮力法是一种简单直接解决问题的方法,常常直接基于问题的描述,所以蛮力法也是最容易应用的方法. 蛮力法所依赖 的基本技术是遍历,即采用一定的策略依次处理待求解问题的所有元素,从 ...

  3. 算法设计与分析实验二:动态规划法实现TSP问题和0/1背包问题

    [实验目的] 1.熟练掌握动态规划思想及教材中相关经典算法. 2.使用动态规划法编程,求解0/1背包问题和TSP问题. TSP问题 一.实验内容: TSP问题是指旅行家要旅行n个城市,要求每个城市经历 ...

  4. 程振波 算法设计与分析_算法分析与设计之动态规划

    动态规划同样是一种将问题分解为求解子问题的方法,不过与分治不同的是,动态规划算法的子问题不是相互独立的,而是有公共的部分,即有重叠子问题,这个时候使用分治算法,将会重复计算公共的子问题,效率很低!而用 ...

  5. 分枝限界法求解0/1背包问题

    问题描述 有n个重量分别为{w1,w2,-,wn}的物品,它们的价值分别为{v1,v2,-,vn},给定一个容量为W的背包. 设计从这些物品中选取一部分物品放入该背包的方案,每个物品要么选中要么不选中 ...

  6. NJUPT算法分析与设计期末考试202.12.1

    NJUPT算法分析与设计期末考试2021.11.24 判断 简答 1.算法是什么?算法的时间复杂度是什么?衡量的原则,标准,工具 2.分支限界法扩展活节点的方式有哪两种,有什么差别? 3.回溯法搜索子 ...

  7. Python算法分析与设计实验:动态规划算法

    Python算法分析与设计实验:动态规划算法 一.实验目的 1.理解动态规划求解优化问题的典型步骤,以及动态规划算法求解计算问题的时间复杂度分析 2.熟练掌握利用动态规划算法求解一维.二维等典型优化问 ...

  8. 算法分析与设计课程复习之回溯法

    算法分析与设计课程复习之回溯法 一.基本思想 1.解空间 设问题的解向量为X=(x1,x2,-,xn) ,xi的取值范围为有穷集Si .把xi的所有可能取值组合,称为问题的解空间.每一个组合是问题的一 ...

  9. 算法分析与设计期末总结(安徽大学)

    目录 前言 一.算法概述 算法与程序 算法复杂度分析 NP完全理论 二.递归与分治策略 递归(自顶向下) 分治策略(自顶向下) 三.动态规划(自底向上) 0-1背包问题:给定n种物品和一背包.物体i的 ...

  10. 算法分析与设计——贪心法实验报告

       算法导论  课程设计 成 绩 题    目:    贪心法 学院班级:        1613013         学    号:      16130130216       姓    名: ...

最新文章

  1. JS如何深度复制对象和数组,避免指针变量引用修改值
  2. ICRA2021|嵌入式系统的鲁棒单目视觉惯性深度补全算法
  3. Appian宣布将Google AI 集成到RPA中
  4. load balancer 配置参考
  5. LPM Sprint 4-13 开发 工作总结
  6. 【ArcGIS微课1000例】0020:关于ArcCatalog,你知道多少?
  7. 如何查找历史线程阻塞原因_吊打面试官!Java多线程并发 108 道题,你能答对多少?...
  8. 【七】jquery之属性attr、 removeAttr、prop[全选全不选及反选]
  9. 第13章 集成学习和随机森林 学习笔记中 oob
  10. 织梦DEDE一键搬迁网站模板数据到DSCMS教程
  11. 使用stm32驱动RC522读取IC卡
  12. ueditor编辑器的坑(视频空白/保存无数据/无法删除/不能插入百度动态地图/有序列表显示问题)
  13. 库卡profinet通讯中文说明书
  14. IoT数据科学与传统数据科学的10个差异
  15. 什么是源端口和目的端口
  16. GRMS_README
  17. 从内容打到社交,头腾大战矛头又指向了游戏
  18. 判别机器大小端,打印int的二进制
  19. i技术会 | 爱奇艺品牌广告算法探索和实践
  20. cadence软件初识

热门文章

  1. vscode插件-格式化代码-工具
  2. 【项目】小帽学堂(十一①)
  3. C#调节windows音量
  4. 在微信小程序中如何下载APP?
  5. buct2018年程序设计实训作业一部分题目解答
  6. 拿下618,京东祭出AI备战双11
  7. css 剪辑图片_css实现图片剪裁
  8. Android开发示例
  9. 服务器 12 种基本故障+排查方法
  10. 转:Windows Shell 编程 第七章_1