Leetcode673 最长递增子序列的个数 贪心DP
首先一个错误的暴力解法,加深对解空间结构的认识:
/** * @Author Niuxy * @Date 2020/6/21 11:30 上午 * @Description 解决问题时先推导一下,问题是否可以被自己解决。 * 若果不行,尝试转化问题。 * 比如找出所有递增序列,并按长度计数 */ public int findNumberOfLIS(int[] nums) { Map<Integer, Integer> m = new HashMap<Integer, Integer>(); int maxLength = 0; for (int i = 0; i < nums.length; i++) { int size = 1; int node = nums[i]; for (int j = i + 1; j < nums.length; j++) { if (nums[j] > node) { node = nums[j]; size++; } } if (size != 1) { maxLength = Math.max(maxLength, size); if (m.containsKey(size)) { m.put(size, m.get(size) + 1); } else { m.put(size, 1); } } } return maxLength == 0 ? nums.length : m.get(maxLength); }
分治优化,沿缓存计算时采用贪心思想,只记录最长子序列:
/** * @Author Niuxy * @Date 2020/6/21 8:35 下午 * @Description 上一个解法的问题在于,逐个元素进行比较,无法覆盖所有解空间。 * 比如 [1,3,3,4] ,选择完第一个元素后,可以选择第二个也可以选择第三个,而上个解法只会考虑选择第二个的情况。 * 另外我们发现,自前向后遍历时,存在许多重复计算。 * 重复计算存在于: * 计算以 num[1] 开头的最长子序列时,后续计算结果可以被计算以 num[n](某个可作为 num[1] 开头的最长子序列的后续元素) * 开头的最长子序列复用 * 那么以 n 为坐标建立缓存,定义 G(n) 可以记录以 nums[n] 开头的最递增子序列的个数 */ public int findNumberOfLIS1(int[] nums) { An[] cache=new An[nums.length]; for (int i = 0; i < nums.length; i++) { G(nums, i,cache); } return reAn.length == 1 ? nums.length : reAn.num; } class An { int length; int num; An(int length, int num) { this.length = length; this.num = num; } } ; An reAn = new An(1, 0); public An G(int[] nums, int n, An[] cache) { if (n > nums.length - 1) { return new An(1, 1); } if (cache[n] != null) { An an = cache[n]; if (an.length == reAn.length) { reAn.num += an.num; } if (an.length > reAn.length) { reAn.length = an.length; reAn.num = an.num; } return an; } int reLen = 1; int reNum = 1; for (int i = n + 1; i < nums.length; i++) { if (nums[i] <= nums[n]) { continue; } An an = G(nums, i, cache); if (an.length == reLen - 1) { reNum += an.num; } else if (an.length >= reLen) { reLen = an.length + 1; reNum = an.num; } } if (reLen == reAn.length) { reAn.num += reNum; } if (reLen > reAn.length) { reAn.length = reLen; reAn.num = reNum; } cache[n] = new An(reLen, reNum); return cache[n]; }
许多题目,直接从题目去定义分治问题很难定义,先用暴力解法找出解空间中的重复结构,问题的定义便会明朗很多。
该题最快的解法为线段树,但是为了练习 DP ,得出上述解法。可以通过,但排名比较靠后:
当你看清人们的真相,于是你知道了,你可以忍受孤独
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构