• 算法——马戏团人塔


    有个马戏团正在设计叠罗汉的表演节目,一个人要站在另一人的肩膀上。出于实际和美观的考虑,在上面的人要比下面的人矮一点且轻一点。已知马戏团每个人的身高和体重,请编写代码计算叠罗汉最多能叠几个人。
    leetcode

    纯DP方法

    解题思路:首先根据身高将数组顺序排列,然后将身高相同的根据体重顺序排列。这样就是求一个二维的最长上升序列的问题。

    利用一个DP数组存储当前元素结尾的最长子序列值,然后从前往后遍历数组,遍历每个元素的时候,再从这个元素开始往前遍历DP数组,寻找身高体重都比自己小的元素,然后取最大值。时间复杂度是N方的。

    这样做在leetcode中会超时。

    class Solution {
        public int bestSeqAtIndex(int[] height, int[] weight) {
            int n = height.length;
            if(n == 0) return 0;
            List<Integer> man = new ArrayList<>();
            int[] f = new int[n];
            int res = 1;
    
            for(int i = 0; i < n; i++) man.add(i);
    
            Collections.sort(man, (b, a) -> height[a] == height[b] ? weight[a] - weight[b] : height[a] - height[b]);
    
            for(int i = 0; i < n; i++) {
                f[i] = 1;
                for(int j = i - 1; j >= 0; j--) {
                    if(height[man.get(j)] > height[man.get(i)] && weight[man.get(j)] > weight[man.get(i)]) {
                        f[i] = Math.max(f[i], f[j] + 1);
                    }
                }
    
                res = Math.max(f[i], res);
            }
    
            return res;
        }
    }
    

    DP + 二分

    解题思路:首先将根据将数组根据身高正序排列,然后将身高相同的根据体重逆序排列。这样,就将问题转化为求体重的最长上升序列的问题了。
    利用一个DP数组存储当前最长的上升的身高队列,然后也是从后前往后遍历数组,每次遍历到一个元素后,就将这个DP数组进行二分,获取当前元素的最恰当的位置。什么是恰当的位置呢?如果这个元素的值在DP数组的范围中,则替换第一个大于等于它的值,如果这个元素大于DP数组的最大值,则直接添加到最后,同时答案加1。这样是为了保证数组保持递增,同时降低元素的值。

    时间复杂度是NlogN 的。不会超时。

    • 为什么身高相同体重需要逆序排列呢?
      当遍历数组的时候,我们当然希望后面的升高越小越好,如果相同的体重,就会出现覆盖的问题,可以让小的覆盖大的,但是不能让大的覆盖小的。
    class Solution {
        public int bestSeqAtIndex(int[] height, int[] weight) {
            int len = height.length;
            int[][] person = new int[len][2];
            for (int i = 0; i < len; ++i)
                person[i] = new int[]{height[i], weight[i]};
            Arrays.sort(person, (a, b) -> a[0] == b[0] ? b[1] - a[1] : a[0] - b[0]);
    
    
            int [] f = new int [person.length];
            int res = 0;
            for(int [] p : person){
                int l = 0, r = res;
                while(l < r){
                    int mid = l + r >> 1;
                    if(f[mid] < p[1]) l = mid + 1;
                    else r = mid;
                }
                f[r] = p[1];
                if(res == r)  res++;
            }
            return res;
        }
    }
    
    
  • 相关阅读:
    跟我一起学算法——贪心算法
    跟我一起学算法——最大流
    跟我一起学算法——红黑树
    设计模式总结
    敏捷开发模型
    TCP安全,SYN Flooding 和 nmap
    ICMP和重定向攻击
    IP安全,DDoS攻击、tearDrop攻击和微小IP碎片攻击
    Netfilter,获取http明文用户名和密码
    Linux常用网络命令
  • 原文地址:https://www.cnblogs.com/lippon/p/14117633.html
Copyright © 2020-2023  润新知