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

递归入手三维动态规划

一和零

https://leetcode.cn/problems/ones-and-zeroes/description/

题目解析

由递归入手, 就是一颗选和不选的树, 我们很好通过 dfs 来写出递归

题目解析
public class 一和零 {public static int zeros;public static int ones;public int findMaxForm(String[] strs, int m, int n) {return dfs(strs,m,n,0);}private int dfs(String[] strs, int m, int n, int pos) {if (pos == strs.length) {return 0;}int ans = Integer.MIN_VALUE;// 不选这个ans = dfs(strs,m,n,pos + 1);// 选这个zerosAndOnes(strs[pos],pos);if (m - zeros >= 0 && n - ones >= 0) {ans = Math.max(ans,dfs(strs,m - zeros,n - ones,pos + 1) + 1);}return ans;}private void zerosAndOnes(String str, int pos) {ones = 0;zeros = 0;for (int i = 0; i < str.length(); i++) {if (str.charAt(i) == '1') {ones++;}}zeros = str.length() - ones;}
}

在这里插入图片描述

不出所料地绝对超时了, 我们进行记忆化搜索进行优化, 挂上缓存, 避免重复的递归

记忆化搜索

我们根据变化的三个变量, 构造出一张表, 开始都是 -1, 每次递归如果不是 -1, 说明之前已经进行过一次递归了, 直接拿缓存的值即可
注意 : 数组的下标 m 和 n 需要增加 1, 因为我们是以数组的下标来进行映射的, 所以数组要扩大 + 1

class Solution {public static int zeros;public static int ones;public int findMaxForm(String[] strs, int m, int n) {int[][][] dp = new int[m + 1][n + 1][strs.length];for (int i = 0; i <= m; i++) {for (int j = 0; j <= n; j++) {for (int k = 0; k < strs.length; k++) {dp[i][j][k] = -1;}}}return dfs(strs,m,n,0,dp);}private int dfs(String[] strs, int m, int n, int pos,int[][][] dp) {if (pos == strs.length) {return 0;}if (dp[m][n][pos] != -1) {return dp[m][n][pos];}int ans = Integer.MIN_VALUE;// 不选这个ans = dfs(strs,m,n,pos + 1,dp);// 选这个zerosAndOnes(strs[pos],pos);if (m - zeros >= 0 && n - ones >= 0) {ans = Math.max(ans,dfs(strs,m - zeros,n - ones,pos + 1,dp) + 1);}dp[m][n][pos] = ans;return ans;}private void zerosAndOnes(String str, int pos) {ones = 0;zeros = 0;for (int i = 0; i < str.length(); i++) {if (str.charAt(i) == '1') {ones++;}}zeros = str.length() - ones;}
}

在这里插入图片描述

当我们挂上缓存后已经可以通过了, 这时候我们由自底向上, 考虑如何实现 dp

动态规划

在这里插入图片描述

根据之前的记忆化搜索, 我们可以发现, i 层是严格依赖于 i + 1 层的值, i + 1 层是没有字符串的, 所以全部都置为 0
逻辑直接抄记忆化搜索中的一些关系, 我们只需要处理好边界条件即可

初始化

最上面一层都是 0, 无需进行初始化

填表顺序

层数从上到小还是从下到上都可以, j, k, 由于当前层是依赖于上一层的位置关系, 所以这个 从前往后还是从后往前进行填表都可以

返回值

返回第 0 层, 因为我们是自底向上的, 前 m 个和前 n 个的最大值
所以返回 return dp[0][m][n]

class Solution {public int zeros;public int ones;public int findMaxForm(String[] strs, int m, int n) {int[][][] dp = new int[strs.length + 1][m + 1][n + 1];for (int i = strs.length - 1; i >= 0; i--) {zerosAndOnes(strs[i]);for (int j = 0; j <= m; j++) {for (int k = 0; k <= n; k++) {dp[i][j][k] = dp[i + 1][j][k];if (j - zeros >= 0 && k - ones >= 0) {dp[i][j][k] = Math.max(dp[i][j][k],dp[i + 1][j - zeros][k - ones] + 1);}}}}return dp[0][m][n];}private void zerosAndOnes(String str) {ones = 0;zeros = 0;for (int i = 0; i < str.length(); i++) {if (str.charAt(i) == '1') {ones++;}}zeros = str.length() - ones;}
}
空间压缩

在这里插入图片描述

  1. 因为每一层是相互依赖于每一层的, 所以我们可以使用一个二维表格来进行填写表格, 先把前面的 i 那一层全部删除掉, 我们考虑下一层的依赖关系即可
  2. 根据图片, 我们知道如果在同一层进行填写的话, 依赖的是左上位置的格子, 所以要晚一点覆盖掉这里的格子, 所以我们要从大到小开始填写, 之前的 if 条件来判断防止越界可以直接加到 for 循环中, 防止不必要的遍历, 至于 i 的遍历顺序 从前到后还是从后到前都可以
class Solution {public int zeros;public int ones;public int findMaxForm(String[] strs, int m, int n) {int[][] dp = new int[m + 1][n + 1];for (int i = strs.length - 1; i >= 0; i--) {zerosAndOnes(strs[i]);for (int j = m; j >= zeros; j--) {for (int k = n; k >= ones; k--) {dp[j][k] = Math.max(dp[j][k],dp[j - zeros][k - ones] + 1);}}}return dp[m][n];}private void zerosAndOnes(String str) {ones = 0;zeros = 0;for (int i = 0; i < str.length(); i++) {if (str.charAt(i) == '1') {ones++;}}zeros = str.length() - ones;}
}

盈利计划

题目解析

也是先从递归入手, 我们写出决策树, 每一个 i 都判断选和不选, 直接先通过暴力递归来找到答案

递归
class Solution {public static int mod = 1000000007;public int profitableSchemes(int n, int minProfit, int[] group, int[] profit) {return dfs1(n,minProfit,group,profit,0);}private int dfs1(int n, int minProfit, int[] group, int[] profit, int pos) {// 没人了if (n <= 0) {return minProfit <= 0 ? 1 : 0;}// 没有工作了if (pos == group.length) {return minProfit <= 0 ? 1 : 0;}// 不干这个工作int ans = 0;ans = dfs1(n,minProfit,group,profit,pos + 1) % mod;// 干这个工作if (n >= group[pos]) {ans = (ans +  dfs1(n - group[pos], minProfit - profit[pos], group,profit, pos + 1) % mod) % mod;}return ans;}
}

在这里插入图片描述

不出意料还是超时了, 但是也就差 10 个用例, 说明我们的做法是正确的

记忆化搜索

我们挂上缓存, 需要注意一个细节, 利润会被我们减到负数有可能, 所以我们需要一个比较, 如果变成负数说明利润已经达到了, 我们把它变成 0 , 我们来修改一下代码, 和上一题的整体步骤是差不多的

class Solution {public static int mod = 1000000007;public int profitableSchemes(int n, int minProfit, int[] group, int[] profit) {int[][][] dp = new int[group.length + 1][n + 1][minProfit + 1];for (int i = 0; i <= group.length; i++) {for (int j = 0; j <= n; j++) {for (int k = 0; k <= minProfit; k++) {dp[i][j][k] = -1;}}}return dfs1(n,minProfit,group,profit,0,dp);}private int dfs1(int n, int minProfit, int[] group, int[] profit, int pos,int[][][] dp ) {// 没人了if (n <= 0) {return minProfit <= 0 ? 1 : 0;}// 没有工作了if (pos == group.length) {return minProfit <= 0 ? 1 : 0;}if (dp[pos][n][minProfit] != -1) {return dp[pos][n][minProfit];}// 不干这个工作int ans = 0;ans = dfs1(n,minProfit,group,profit,pos + 1,dp) % mod;// 干这个工作if (n >= group[pos]) {ans = (ans +  dfs1(n - group[pos],Math.max(minProfit - profit[pos],0),group,profit,pos + 1,dp) % mod) % mod;}dp[pos][n][minProfit] = ans;return ans;}
}
动态规划
初始化

最上面一层都是 0, 无需进行初始化
根据图片, 并且每一层都依赖上一层, 所以我们只需要初始化最上面一层即可, 这个和上一题是一样的, 当利润为 0 的时候发现是填入 1, 我们根据记忆化搜索的返回条件可以知道, 所以我们就知道, 最高层的利润为 0 的点填入 1
填表顺序

填表顺序

根据图上画的, 依赖上一层, 从左向右, 从右向左填 j, k都行

返回值

自底向上填写, 最下层满足 dp[0][n][minProfit] 这个可以根据记忆化搜索的返回值得知

class Solution {public static int mod = 1000000007;public int profitableSchemes(int n, int minProfit, int[] group, int[] profit) {int[][][] dp = new int[group.length + 1][n + 1][minProfit + 1];for (int j = 0; j <= n; j++) {dp[group.length][j][0] = 1;}for (int i = group.length - 1; i >= 0; i--) {for (int j = n; j >= 0; j--) {for (int k = minProfit; k >= 0; k--) {dp[i][j][k] = dp[i + 1][j][k] % mod;if (j >= group[i]) {dp[i][j][k] = (dp[i][j][k] + dp[i + 1][j - group[i]][Math.max(k - profit[i],0)] % mod) % mod;}}}}return dp[0][n][minProfit];}
}
空间压缩

在这里插入图片描述

根据上面那张图, 我们用一个二维表来填写, 由于依赖左上方的位置, 所以我们从右下角从右向左, 从上到下进行填写

  1. 先把所有的前缀 i 都删掉
  2. 改一下遍历顺序
  3. 把 if 里的提到 for 循环中, 不提也可以
class Solution {public static int mod = 1000000007;public int profitableSchemes(int n, int minProfit, int[] group, int[] profit) {int[][] dp = new int[n + 1][minProfit + 1];for (int j = 0; j <= n; j++) {dp[j][0] = 1;}for (int i = group.length - 1; i >= 0; i--) {for (int j = n; j >= group[i]; j--) {for (int k = minProfit; k >= 0; k--) {dp[j][k] = (dp[j][k] + dp[j - group[i]][Math.max(k - profit[i],0)] % mod) % mod;}}}return dp[n][minProfit];}
}

此处取模可以去掉一个, 两层是怕溢出, 此题不会溢出, 去掉会快 8 秒左右


http://www.mrgr.cn/news/93226.html

相关文章:

  • Idea配置注释模板
  • 用CMake编译glfw进行OpenGL配置,在Visual Studio上运行
  • 图解MOE大模型的7个核心问题并探讨DeepSeekMoE的专家机制创新
  • 5年前问题的答案,如何造统计信息
  • Mybatis中的设计模式
  • 安装微软最新原版系统,配置好系统驱动并保留OOBE全新体验
  • JAVA入门——反射
  • 《Operating System Concepts》阅读笔记:p188-p199
  • 蓝桥杯C组真题——巧克力
  • Linux软件包管理
  • HTTP 黑科技
  • uniapp:小程序将base64图片字符串保存到手机相册
  • 免费分享一个软件SKUA-GOCAD-2022版本
  • C++11中atomic
  • 大模型在呼吸衰竭预测及围手术期方案制定中的应用研究
  • 计算机网络核心知识点:信道容量、OSI模型与调制技术详解
  • 鸿蒙与DeepSeek深度整合:构建下一代智能操作系统生态
  • iOS安全和逆向系列教程 第8篇:iOS应用动态分析与Hook技术
  • iOS安全和逆向系列教程 第2篇: iOS系统架构详解 - 逆向工程的基石
  • iOS安全和逆向系列教程 第3篇:搭建iOS逆向开发环境 (上) - 工具链与基础配置