354. 俄罗斯套娃信封问题

90 阅读2分钟

题目:
给你一个二维整数数组 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最低的,这样可以套更多的信封。