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