# Question

Formatted question description: https://leetcode.ca/all/354.html

You are given a 2D array of integers envelopes where envelopes[i] = [wi, hi] represents the width and the height of an envelope.

One envelope can fit into another if and only if both the width and height of one envelope are greater than the other envelope's width and height.

Return the maximum number of envelopes you can Russian doll (i.e., put one inside the other).

Note: You cannot rotate an envelope.

Example 1:

Input: envelopes = [[5,4],[6,4],[6,7],[2,3]]
Output: 3
Explanation: The maximum number of envelopes you can Russian doll is 3 ([2,3] => [5,4] => [6,7]).


Example 2:

Input: envelopes = [[1,1],[1,1],[1,1]]
Output: 1


Constraints:

• 1 <= envelopes.length <= 105
• envelopes[i].length == 2
• 1 <= wi, hi <= 105

# Algorithm

Consider a situation [100,100],[200,200], [1,300],[2,400],[3,500]. If you only update x in p(x,y) every time, then the next three will be thrown away, and the maximum value is 2, while correct reulst should be 3.

First, we must sort all the envelopes from smallest to largest.

• First row according to the width from small to large,
• If the width is the same, then the height is smaller and then the front,

Then we start to traverse, for each envelope, we traverse all the envelopes before it,

• If the length and width of the current envelope are larger than those of the previous envelope, then we update the dp array by dp[i] = max(dp[i], dp[j] + 1).
• Then every time we traverse an envelope, we update the result

### Follow up of this question

If the envelope can be rotated. What is the longest sequence?

The answer is to enqueue <3,4>, then <4,3> also enqueue, and then find the longest sequence.

# Code

• import java.util.Arrays;

public class Russian_Doll_Envelopes {

/*
要考虑一种情况 [100,100],[200,200], [1,300],[2,400],[3,500]
如果只是每次更新p(x,y)中的x，那么后面三个就会都被扔掉，最大值是2
*/
class Solution {

public int maxEnvelopes(int[][] envelopes) {

if (envelopes == null || envelopes.length == 0) {
return 0;
}

int[] dp = new int[envelopes.length]; // at index=i, longest count
int maxLen = 1;

Arrays.sort(envelopes, (a,b)-> a == b? a - b : a - b);

for (int right = 0; right < envelopes.length; right++) {

dp[right] = 1;

for (int left = 0; left < right; left++) {

if (envelopes[left] < envelopes[right] && envelopes[left] < envelopes[right]) {
dp[right] = Math.max(dp[right], dp[left] + 1);
maxLen = Math.max(maxLen, dp[right]);
}

}
}

return maxLen;
}
}
}

############

class Solution {
public int maxEnvelopes(int[][] envelopes) {
Arrays.sort(envelopes, (a, b) -> { return a == b ? b - a : a - b; });
int n = envelopes.length;
int[] d = new int[n + 1];
d = envelopes;
int size = 1;
for (int i = 1; i < n; ++i) {
int x = envelopes[i];
if (x > d[size]) {
d[++size] = x;
} else {
int left = 1, right = size;
while (left < right) {
int mid = (left + right) >> 1;
if (d[mid] >= x) {
right = mid;
} else {
left = mid + 1;
}
}
int p = d[left] >= x ? left : 1;
d[p] = x;
}
}
return size;
}
}

• // OJ: https://leetcode.com/problems/russian-doll-envelopes/
// Time: O(NlogN)
// Space: O(N)
// Ref: https://leetcode.com/problems/russian-doll-envelopes/discuss/82763/Java-NLogN-Solution-with-Explanation
class Solution {
public:
int maxEnvelopes(vector<vector<int>>& A) {
sort(begin(A), end(A), [](auto &a, auto &b) { return a != b ? a < b : a > b; });
vector<int> dp;
for (auto &v : A) {
auto it = lower_bound(begin(dp), end(dp), v);
if (it == end(dp)) dp.push_back(v);
else *it = v;
}
return dp.size();
}
};

• '''
>>> envelopes = [[100,100],[200,200], [1,300],[2,400],[2,401], [2,402], [3,500]]
>>> envelopes.sort(key=lambda key: (key, -key))
>>> envelopes
[[1, 300], [2, 402], [2, 401], [2, 400], [3, 500], [100, 100], [200, 200]]
>>>
>>>
>>> tails = []
>>> bisect.bisect_right(tails, envelopes)
0
>>> tails.append(envelopes)
>>>
>>> bisect.bisect_right(tails, envelopes)
1
>>> tails.append(envelopes)
>>> tails
[300, 402]
>>>
>>> bisect.bisect_right(tails, envelopes) # [2, 401]
1
'''

# w increasing-ordered, if w ties then h decreasing ordered. then it's the question of Longest Increasing Subsequence for all h (As of LC-300 Longest Increasing Subsequence)

class Solution(object):
def maxEnvelopes(self, envelopes):
"""
:type envelopes: List[List[int]]
:rtype: int
"""
# why -key, not key?
# so that when [[1, 300], [2, 402], [2, 401], [2, 400], [3, 500], [100, 100], [200, 200]], for w=2, we want to keep [2,400] not keep [2,402] to potentially have longer increasing subsequence
envelopes.sort(key=lambda key: (key, -key))
tails = []
for i in range(0, len(envelopes)):
idx = bisect.bisect_left(tails, envelopes[i])
if idx == len(tails):
tails.append(envelopes[i])
else:
tails[idx] = envelopes[i]
return len(tails)

class Solution(object):
def maxEnvelopes(self, envelopes):
"""
:type envelopes: List[List[int]]
:rtype: int
"""
# why -key, not key?
# eg. input [[1, 300], [2, 402], [2, 401], [2, 400], [3, 500], [100, 100], [200, 200]],
# for w=2, we want to keep [2,400] not keep [2,402] to potentially have longer increasing subsequence
envelopes.sort(key=lambda key: (key, -key))
tails = []
for i in range(0, len(envelopes)):
'''
@note: below line different from above
if use bisect_right(), then need to add extra if check for duplication
'''
idx = bisect.bisect_right(tails, envelopes[i])
if idx - 1 >= 0 and tails[idx - 1] == envelopes[i]:
continue # avoid de-dup
if idx == len(tails):
tails.append(envelopes[i])
else:
tails[idx] = envelopes[i]
return len(tails)

# O(N^2), overlimit if large input
class Solution:
def maxEnvelopes(self, envelopes: List[List[int]]) -> int:
if not envelopes:
return 0

n = len(envelopes)
dp =  * n
max_len = 1

envelopes.sort(key=lambda x: (x, -x))

for i in range(1, n):
for j in range(i):
if envelopes[j] < envelopes[i] and envelopes[j] < envelopes[i]:
dp[i] = max(dp[i], dp[j] + 1)
max_len = max(max_len, dp[i])

return max_len

############

class Solution:
def maxEnvelopes(self, envelopes: List[List[int]]) -> int:
envelopes.sort(key=lambda x: (x, -x))
d = [envelopes]
for _, h in envelopes[1:]:
if h > d[-1]:
d.append(h)
else:
idx = bisect_left(d, h)
if idx == len(d):
idx = 0
d[idx] = h
return len(d)

• func maxEnvelopes(envelopes [][]int) int {
sort.Slice(envelopes, func(i, j int) bool {
if envelopes[i] != envelopes[j] {
return envelopes[i] < envelopes[j]
}
return envelopes[j] < envelopes[i]
})
n := len(envelopes)
d := make([]int, n+1)
d = envelopes
size := 1
for _, e := range envelopes[1:] {
x := e
if x > d[size] {
size++
d[size] = x
} else {
left, right := 1, size
for left < right {
mid := (left + right) >> 1
if d[mid] >= x {
right = mid
} else {
left = mid + 1
}
}
if d[left] < x {
left = 1
}
d[left] = x
}
}
return size
}