首先一个错误的暴力解法,加深对解空间结构的认识:
/** * @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 ,得出上述解法。可以通过,但排名比较靠后: