leetcode
leetcode 2851 ~ 2900
马戏团人塔

马戏团人塔

难度:

标签:

题目描述

A circus is designing a tower routine consisting of people standing atop one anoth­er's shoulders. For practical and aesthetic reasons, each person must be both shorter and lighter than the person below him or her. Given the heights and weights of each person in the circus, write a method to compute the largest possible number of people in such a tower.

Example:

Input: height = [65,70,56,75,60,68] weight = [100,150,90,190,95,110]
Output: 6
Explanation: The longest tower is length 6 and includes from top to bottom: (56,90), (60,95), (65,100), (68,110), (70,150), (75,190)

Note:

  • height.length == weight.length <= 10000

代码结果

运行时间: 146 ms, 内存: 18.4 MB


// 题目思路:
// 同样的,我们需要根据身高和体重来构建一个最长递增子序列。
// 使用Java Stream来对数据进行排序,并利用二分查找来寻找LIS。

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class CircusTowerStream {
    public int maxEnvelopes(int[] height, int[] weight) {
        List<int[]> people = Arrays.stream(height)
            .boxed()
            .map(i -> new int[]{height[i], weight[i]})
            .sorted((a, b) -> a[0] == b[0] ? b[1] - a[1] : a[0] - b[0])
            .collect(Collectors.toList());

        int[] dp = new int[people.size()];
        int maxLength = 0;

        for (int[] person : people) {
            int weightValue = person[1];
            int left = 0, right = maxLength;
            while (left < right) {
                int mid = (left + right) / 2;
                if (dp[mid] < weightValue) left = mid + 1;
                else right = mid;
            }
            dp[left] = weightValue;
            if (left == maxLength) maxLength++;
        }

        return maxLength;
    }

    public static void main(String[] args) {
        CircusTowerStream tower = new CircusTowerStream();
        int[] height = {65, 70, 56, 75, 60, 68};
        int[] weight = {100, 150, 90, 190, 95, 110};
        System.out.println(tower.maxEnvelopes(height, weight));
    }
}

解释

方法:

首先,对输入的身高和体重进行处理,按身高升序、体重降序的方式进行排序,这样可以确保在相同身高的情况下,体重较重的人排在前面。然后,使用一个动态数组 d_tail 来记录当前可以叠罗汉的最长序列,初始时只包含第一个人的体重。接着,遍历排序后的每个人,对于每个人,检查他们的体重是否可以放在当前的叠罗汉序列的末尾,如果可以,则将他们的体重添加到序列末尾;否则,使用二分查找在序列中找到一个位置,使得该位置之前的所有人的体重都小于当前人的体重,并将当前人的体重放在这个位置。最后,返回 d_tail 的长度,即为最多能叠的人数。

时间复杂度:

O(n log n)

空间复杂度:

O(n)

代码细节讲解

🦆
为什么在排序时要先按身高升序排列,接着对于相同身高的人按体重降序排列?
这种排序策略是为了简化问题。首先按身高升序排列确保在处理体重时,只需要考虑身高相同或更高的人。如果身高相同,再按体重降序排列可以确保在同一身高内,体重较重的人排在前面,这样当我们构建基于体重的最长递增子序列时,可以避免身高相同但体重较轻的人被错误地叠加到身高相同但体重较重的人上面。这样,我们只需关注体重的单调性,而不需要担心身高对叠罗汉顺序的影响。
🦆
在使用动态数组 d_tail 记录最长序列时,这个数组的每个元素代表的是什么含义?
在这个问题中,动态数组 d_tail 的每个元素代表在当前考虑的人员中,能够组成的最长递增子序列的体重值。数组的长度表示最长递增子序列的长度,而每个位置上的体重值表示在构建这个序列时,每个阶段可容纳的最小体重。这样,d_tail 数组不仅记录了最长序列的长度,还通过每个位置的体重值,保证了序列的最优性(即尽可能让序列中的体重值较小,这样有利于后续更大的体重值加入到序列中)。
🦆
为什么当新加入的人的体重大于 d_tail 数组的最后一个元素时,选择将该体重直接添加到 d_tail 的末尾?
当新加入的人的体重大于 d_tail 数组的最后一个元素时,这表示当前考虑的这个人可以放在已有的最长递增子序列之后,形成一个更长的递增序列。由于 d_tail 数组维护的是一个体重递增的序列,新加入的体重更大,直接添加到末尾可以延长这个序列而不破坏其递增的性质。这反映了动态规划中逐步构建解决方案的思想,即利用已有的最优解构建新的更优解。

相关问题