当前位置: 首页 > news >正文

【算法训练营Day26】动态规划part2

文章目录

  • 01背包理论基础(二维)
  • 01背包典例(装满背包的最大价值)
  • 01背包理论基础(一维)
  • 分割等和子集(能不能装满背包)
  • 最后一块石头的重量II(背包最多能装多少)
  • 目标和(装满背包有多少种方式)
  • 一和零(装满背包最多用多少个物品)

01背包理论基础(二维)

01背包代表的就是有多种不同价值、重量的物品,背包承载的物品重量有限,每个物品的数量只有一个,怎么放进背包价值最高。

01背包的dp数组可以是二维的也可以是一维的,先说二维更好理解。

我们从dp四部曲来分析背包问题:

  • dp数组及下标的含义:在二维dp数组中,一个维度是物品,另一个维度代表背包的容量。dp[i][j]表示把0 ~ i号物品放到容量为j的背包中最大的价值是多少。
  • 递推公式:dp[i][j]可以从dp[i - 1][]推导而来,一种是有物品i,另一种是没有物品i。如果没有物品i,那么可以直接从dp[i - 1][j]推导而来,如果有物品i,那么就需要从dp[i - 1][j - weight[i]] + value[i]推导得到。所以最终两者取最大值就是dp[i][j] = max(dp[i - 1][j],dp[i - 1][j - weight[i]] + value[i])
  • 初始化:首先根据含义j等于0的情况背包什么都装不了,所以可以将dp[i][0]全部初始化为0。再根据递推式,我们肯定需要dp[0][j]的数据,那么j < weight[0]的时候dp[0][j]为0,j >= weight[0]的时候dp[0][j] = value[0]。其余位置使用默认值0就可以。
  • 遍历顺序:根据递推式,dp[i][j]依赖上方与左上方的数据,先遍历物品,再遍历背包,更好理解。先遍历背包,再遍历物品也可以。

在实际的问题中很少会出现直接的01背包问题,要将问题进行转化,如果能转化到背包的最大价值,那么就可以尝试使用背包问题的解法。

01背包典例(装满背包的最大价值)

题目链接:46. 携带研究材料

代码如下,要注意的是背包的容量是从0开始计算的,所以对应的长度要 + 1;

import java.util.*;public class Main {public static void main(String[] args){Scanner in = new Scanner(System.in);int item = in.nextInt();int bag = in.nextInt();int[] space = new int[item];int[] values = new int[item];for(int i = 0;i < item;i++) space[i] = in.nextInt();for(int i = 0;i < item;i++) values[i] = in.nextInt();//初始化int[][] dp = new int[item][bag + 1];for(int i = 1;i <= bag;i++) {if(i < space[0]) dp[0][i] = 0;else dp[0][i] = values[0];}//遍历for(int i = 1;i < item;i++) {for(int j = 1;j <= bag;j++) {if(j < space[i]) dp[i][j] = dp[i - 1][j];else dp[i][j] = Math.max(dp[i - 1][j],dp[i - 1][j - space[i]] + values[i]);}}System.out.println(dp[item - 1][bag]);}
}

01背包理论基础(一维)

通过递推式我们可以知道,当前层的数据只与上一层有关,那么我们其实干脆就可以在上一行的基础上进行更新,而无需新启一行。这就是滚动数组,通过滚动数组我们可以将二维的dp数组优化到一维。

从dp四部曲来分析的话,遍历这一步有很大的变化:

  • 只能倒序遍历:这是为了保证物品i只被添加一次。加入我们还是使用正序遍历,那么我们的dp数组随着遍历的进行会出现左边一部分对应的是物品i的情况,而右半部分对应的是物品i - 1的情况,这样我们在进行正序递推的时候可能会出现需要物品i - 1的数据已经被物品i给覆盖了
  • 只能先遍历物品再遍历背包

代码如下:

import java.util.*;public class Main {public static void main(String[] args){Scanner in = new Scanner(System.in);int item = in.nextInt();int bag = in.nextInt();int[] space = new int[item];int[] values = new int[item];for(int i = 0;i < item;i++) space[i] = in.nextInt();for(int i = 0;i < item;i++) values[i] = in.nextInt();//初始化int[] dp = new int[bag + 1];//遍历for(int i = 0;i < item;i++) {for(int j = bag;j > 0;j--) {if(j >= space[i]) dp[j] = Math.max(dp[j],dp[j - space[i]] + values[i]);}}System.out.println(dp[bag]);}
}

分割等和子集(能不能装满背包)

题目链接:416. 分割等和子集

解题思路:

这个问题可以转化为数组中的元素能否构成target,target是数组元素和的一半。因为数组的元素只能使用一次,将数字的值当作重量以及价值,也就是说看容量为target的背包能否装满。再转化一下:也就是看背包的最大价值,如果最大为target,就说明装满了。这个问题可以使用01背包来解决。

解题代码:

class Solution {public boolean canPartition(int[] nums) {int item = nums.length;int sum = 0;for(int num : nums) sum += num;if(sum % 2 != 0) return false;int bag = sum / 2;int[] dp = new int[bag + 1];for(int i = 0;i < item;i++) {for(int j = bag;j > 0;j--) {if(j >= nums[i]) dp[j] = Math.max(dp[j - nums[i]] + nums[i],dp[j]);}if(dp[bag] == bag) return true;}return false;}
}

最后一块石头的重量II(背包最多能装多少)

题目链接:1049. 最后一块石头的重量 II

解题思路:

本题乍一看和动态规划没什么联系,我们可以将问题转化一下。如果要让剩下的石头质量尽量地小,那么我们需要将石头分为尽量相等的两堆相撞即可。这就与上一题有很大的相似性,只不过上一题可以严格的相等。

代码如下:

class Solution {public int lastStoneWeightII(int[] stones) {if(stones.length == 1) return stones[0];int item = stones.length;int sum = 0;for(int stone : stones) sum += stone;int bag = sum / 2;int[] dp = new int[bag + 1];int min = bag;for(int i = 0;i < item;i++) {for(int j = bag;j > 0;j--) {if(j >= stones[i]) dp[j] = Math.max(dp[j - stones[i]] + stones[i],dp[j]);}int result = Math.abs(dp[bag] - (sum - dp[bag]));if(result < min) min = result;}return min;}
}

目标和(装满背包有多少种方式)

题目链接:494. 目标和

解题逻辑:

将下面两个式子联立,得到正数 = (target + sum)/ 2

  • 正数 - 负数 = target
  • 正数 + 负数 = sum

也就是说我们我们只用选取数组中的正数,正好达到(target + sum)/ 2即可。这也就转化成了想要将(target + sum)/ 2 容量的背包正好装满有多少种方法?

从dp四部曲来看:

  • dp数组及下标的含义:dp[j]表示能将j容量的背包装满的方式
  • 递推关系式:dp[j] = dp[j] + dp[j - nums[i]]。不放物品与放物品两种情况,不放物品就有dp[j]方式,放物品就有dp[j - nums[i]]方式
  • 初始化方式:i = 0的时候,将dp[0]初始化为1,代表不放物品装满。dp[nums[0]]++,表示放当前物品装满;
  • 遍历方式:从后往前遍历

代码如下:

class Solution {public int findTargetSumWays(int[] nums, int target) {int sum = 0;for(int num : nums) sum += num;if((sum + target) % 2 != 0) return 0;int bag = (sum + target) / 2;if(bag < 0) return 0;int[] dp = new int[bag + 1];//初始化dp[0] = 1;if(nums[0] <= bag) dp[nums[0]]++;//遍历for(int i = 1;i < nums.length;i++) {for(int j = bag;j >= 0;j--) {if(nums[i] <= j) dp[j] = dp[j] + dp[j - nums[i]]; }}return dp[bag];}
}

一和零(装满背包最多用多少个物品)

题目链接:474. 一和零

解题逻辑:

本题的一大特点就是背包具有多个维度对0的个数,1的个数都有限制。那么我们的dp数组就需要用二维的。

接下来从dp四部曲分析:

  • dp数组以及下标的含义:dp[i][j]表示该背包最多允许i个0,j个1时最多能装几个元素
  • 递推公式:dp[i][j] = max(dp[i - m][j - n] + 1,dp[i][j])
  • 初始化:全部初始化为0
  • 遍历:从下往上,从右往左。防止当前字符串被重复添加。

解题代码:

class Solution {public int findMaxForm(String[] strs, int m, int n) {int[][] dp = new int[m + 1][n + 1];for(String str : strs) {int num0 = 0;int num1 = 0;for(char c : str.toCharArray()) {if(c == '0') num0++;else num1++;}for(int i = m;i >= 0;i--) {for(int j = n;j >= 0;j--) {if(i - num0 >= 0 && j - num1 >= 0) {dp[i][j] = Math.max(dp[i - num0][j - num1] + 1,dp[i][j]);}}}}return dp[m][n];}
}
http://www.dtcms.com/a/411660.html

相关文章:

  • 河北衡水建设网站公司电话wordpress ajax登录插件
  • 网站源码怎么搭建最新新闻热点事件2023年10月
  • 城乡建设部网站广州市国外学校网站设计
  • 泊头网站建设公司wordpress删除主题之后
  • 一站式营销平台wordpress学校网站模板
  • LeetCode 算法题【简单】338. 比特位计数
  • 买房网站排名福州做网站建设公司
  • 爱思强交付第100套G10-SiC系统
  • 网站的建设要多少钱求推荐专门做借条的网站
  • 在线旅游攻略网站建设方案做网站要注册第35类商标吗
  • RocketMQ 核心知识整理:工作原理、常用命令与常见问题解决
  • 做养生网站怎么赚钱麻涌建设网站
  • 域名备案 没有网站网站建设意见建议表
  • Unity-Statemachinebehaviour状态机行为脚本
  • 网站问题图片房子网站有哪些
  • 孝感应城网站建设长春网站建设 找源晟
  • 如何设置网站服务器常州做网站哪家便宜
  • 单片机引脚的高电平和低电平范围值
  • 设计师可以做兼职的网站创建网站的基本步骤
  • 网站后台开发做什么凡科网网站建设
  • 什么是合同管理系统?6个核心功能介绍
  • 数据采集技术:03 有关实时采集
  • 双有源桥DAB仿真控制simulink模型大合集,simulink仿真模型。
  • 石家庄的网站的公司沈阳个人网站制作
  • 网站建设属于移动互联网青岛网站seo诊断
  • Mish激活函数:一种自正则化的非单调神经激活函数
  • Stanford CS336 assignment1(上)
  • 做网站一般几个人完成做简历的网站 知乎
  • 【思考】作为“纯硬件”的ADC芯片,它是如何识别并处理SPI命令的?
  • 申请自助建站多点网络网站制作系统