题目:
给你一个二维整数数组 envelopes ,其中 envelopes[i] = [wi, hi] ,表示第 i 个信封的宽度和高度。
当另一个信封的宽度和高度都比这个信封大的时候,这个信封就可以放进另一个信封里,如同俄罗斯套娃一样。
请计算 最多能有多少个 信封能组成一组“俄罗斯套娃”信封(即可以把一个信封放到另一个信封里面)。
注意:不允许旋转信封。
算法:
方法一:朴素动态规划解法
func maxEnvelopes(envelopes [][]int) int {
sort.Slice(envelopes, func(i, j int) bool {
return envelopes[i][0] < envelopes[j][0]
})
dp := make([]int, len(envelopes))
for i := range dp {
dp[i] = 1
}
ans := dp[0]
for i := 1; i < len(envelopes); i ++ {
for j := 0; j < i; j ++ {
if envelopes[i][0] > envelopes[j][0] && envelopes[i][1] > envelopes[j][1] {
dp[i] = max(dp[i], dp[j] + 1)
}
}
ans = max(ans, dp[i])
}
return ans
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
方法二:动态规划
信封有w,h两个维度,将w按从小到大排序,相等的w只能选择一个信封,相等的w中选择h最大的(为什么不选最小的呢?其实是会对相等w的每个h都选择一遍,求最大值,f[i] = max(f[j]) + 1)。
即按照w升序,h降序排列信封。每个信封结尾的最大套娃数量:
f[i] = max(f[j]) + 1 ; j < i && h[j] < h[i]
func maxEnvelopes(envelopes [][]int) int {
sort.Slice(envelopes, func(i, j int) bool {
if envelopes[i][0] == envelopes[j][0] {
return envelopes[i][1] > envelopes[j][1]
}
return envelopes[i][0] < envelopes[j][0]
})
f := make([]int, len(envelopes))
for i := range f {
f[i] = 1
}
ans := f[0]
for i := 1; i < len(envelopes); i ++ {
for j := 0; j < i; j ++ {
if envelopes[j][1] < envelopes[i][1] {
f[i] = max(f[i], f[j]+ 1)
}
}
ans = max(ans, f[i])
}
return ans
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
方法三:二分+动态规划
func maxEnvelopes(envelopes [][]int) int {
sort.Slice(envelopes, func(i, j int) bool {
if envelopes[i][0] == envelopes[j][0] {
return envelopes[i][1] > envelopes[j][1]
}
return envelopes[i][0] < envelopes[j][0]
})
ans := make([]int, 0)
for i := 0; i < len(envelopes); i ++ {
index := sort.SearchInts(ans, envelopes[i][1])
if index < len(ans) {
ans[index] = envelopes[i][1]
} else {
ans = append(ans, envelopes[i][1])
}
}
return len(ans)
}
输出如下:
排序后的envelopes数组:
[[2 3] [5 4] [6 7] [6 5] [7 6] [8 8]]
ans数组
[3]
[3 4]
[3 4 7]
[3 4 5]
[3 4 5 6]
[3 4 5 6 8]
可以看到,因为对envelopes进行了w升序,h降序进行了排序,sort.SearchInts找到的要么是ans数组的最后一个元素位置,要么是大于ans最后一个元素的位置。
本质上就是对于w相同的信封,通过sort.SearchInts优先选择h最低的,这样可以套更多的信封。