• 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 ,得出上述解法。可以通过,但排名比较靠后:

  • 相关阅读:
    Java笔记(一)Eclipse 操作MySQL数据库的效率问题
    C# 笔记(六)关于switch 语句
    C# 笔记(五)关于static
    arcgis server 9.3初步
    C# 笔记(三)关于结构体变量
    OpenBSD随笔(一)
    Windows 脚本WSH
    Java笔记(二)Eclipse 连接SQlServer
    ArcGIS Server 9.3 JavaScript API实战(二)一个具体的小系统示例介绍
    C#笔记(二)类型转换
  • 原文地址:https://www.cnblogs.com/niuyourou/p/13174949.html
Copyright © 2020-2023  润新知