• 1、线性DP 354. 俄罗斯套娃信封问题


    354. 俄罗斯套娃信封问题

    https://leetcode-cn.com/problems/russian-doll-envelopes/

    算法分析

    首先我们从两种情况来讨论这个问题:

    • w无重复值(即信封的宽度每个信封都不一样)
    • w可以重复(即信封的宽度存在一样的,题目就是这种情况)

    针对情况I

    当每个信封的宽度和高度不一样时,我们可以对信封按照宽度从小到大进行排序,比如针对信封[[3,2],[2, 4],[4,3],[5, 6],[6,5]排序后变为
    w: 2 -> 3 -> 4 -> 5 -> 6
    h: 4 -> 2 -> 3 -> 6 -> 5

    此时,因为信封的宽度w已经是从小到大排列了,要想信封可以套,这要求关于信封高度h的数组[4, 2, 3, 6, 5]是的子序列是递增的,且要求是最长的(题目要求的是最多的信封),所以可以转化为另一个问题:给定数组,求它的最长递增子序列(也称最长上升子序列)。关于这个问题在leetcode300有具体描述。


    针对情况Ⅱ

    对于情况Ⅱ,我们首先像情况I一样考虑,对信封的宽度w按从小到大排序,那么此时面临一个问题,对于相同宽度的信封的高h怎么进行排序,如果我们也按照从小到大排序,那么此时按照信封高求出来的最长递增子序列有可能存在宽度w相同的情况。
    举个栗子:
    当宽度w = 1时, 此时有3个信封,h = 2, 3, 4
    当宽度w = 2时,此时有两个信封,h = 3, 6
    按照上面的排序方式排序后,
    w: 1 -> 1 -> 1 -> 2 -> 2
    h: 2 -> 3 -> 4 -> 3 -> 6
    此时数组h的最长递增子序列为[2, 3, 4, 6]显然不符合条件。所以这种排序方式是错误的。

    那么正确的排序方式是什么样的呢,就是当w相同时,h逆序,从大到小排列,这样你可以想一下,针对h求出来的最长递增子序列不会存在w相等,而h递增的情况,因为w相同的时候,右边的数总是小于等于左边的数,不会出现在最长递增子序列里面。
    还是上面那个栗子排序后:
    w: 1 -> 1 -> 1 -> 2 -> 2
    h: 4 -> 3 -> 2 -> 6 -> 3
    此时数组h的最长递增子序列长度为2([4, 6]或[3, 6]或者其他),即最多有两个信封可以套。


     1 //二维数组排序
     2 type pair struct{
     3     x int
     4     y int
     5 }
     6 
     7 type Type []pair
     8 
     9 func (t Type) Len() int{
    10     return len(t)
    11 }
    12 
    13 func (t Type) Less(i,j int) bool{
    14     if t[i].x < t[j].x {
    15         return true
    16     }else if t[i].x > t[j].x{
    17         return false
    18     }else{
    19         //相同x值,逆序
    20         return t[i].y > t[j].y
    21     }
    22 }
    23 
    24 func (t Type) Swap(i,j int) {
    25     t[i],t[j] = t[j],t[i]
    26 }
    27 
    28 
    29 func maxEnvelopes(envelopes [][]int) int {
    30     n := len(envelopes)
    31     if n<= 1{
    32         return n
    33     }
    34     var p Type
    35     for i:=0;i<n;i++{
    36         p = append(p,pair{envelopes[i][0],envelopes[i][1]})
    37     }
    38     sort.Sort(p)
    39     var h []int
    40     for i:=0;i<n;i++{
    41         h = append(h,p[i].y)
    42     }
    43     //对h数组进行LIS
    44     return LIS(h)
    45 }
    46 func MAX(i,j int) int{
    47     if i<j{
    48         return j
    49     }else{
    50         return i
    51     }
    52 }
    53 //2 3 5 1 3 4
    54 func LIS(h []int) int{
    55     n := len(h)
    56     dp := make([]int,n)
    57     dp[0] = 1
    58     res := 0
    59     for i:=1;i<n;i++{
    60         m := 0
    61         for j:=0;j<i;j++{
    62             if h[j] < h[i]{
    63                 m = MAX(m,dp[j])
    64             }
    65             dp[i] = m+1
    66         }
    67         res = MAX(dp[i],res)
    68     }
    69     return res
    70 }
    优化1、排序优化  2、LIS算法优化
    排序直接对[][]int数组进行排序
    type Env [][]int
    
    func (e Env) Len() int{
    	return len(e)
    }
    
    func (e Env) Less(i,j int) bool{
    	if e[i][0] == e[j][0]{
            return e[i][1] > e[j][1]
        }
        return e[i][0] < e[j][0]
    }
    
    func (e Env) Swap(i,j int) {
    	e[i],e[j] = e[j],e[i]
    }
    
    func maxEnvelopes(envelopes [][]int) int {
    	n := len(envelopes)
    	if n<= 1{
    		return n
    	}
    	sort.Sort(Env(envelopes))
    	var h []int
    	for i:=0;i<n;i++{
    		h = append(h,envelopes[i][1])
    	}
    	//对h数组进行LIS
    	return LIS(h)
    }
    func MAX(i,j int) int{
    	if i<j{
    		return j
    	}else{
    		return i
    	}
    }
    //实际上逻辑是这样: 数组nums中的数nums[i]在 ends数组中二分查找第一个比他大的数并替换,若没有找到,则将nums[i]加在ends末尾,并且dp[i]=ends里面arr[i]的下标+1。
    func LIS(nums []int) int{
    	n := len(nums)
    	if n<= 1{
    		return n
    	}
    	dp := make([]int,n)
    	//这里有个go 切片的隐藏bug。一开始ends := make([]int,n)了,这样的话,ends = append(ends,nums[i])是从第n+1个开始append
    	//这里非常容易出错,因为right = len(ends)-1这个大坑。
    	//这里可以变换下:right = max(left,right).
    	ends := make([]int,1)
    	dp[0] = 1
    	ends[0] = nums[0]
    	res := 0
    	for i:=1;i<n;i++{
    		left , right := 0,len(ends)-1
    		for left <= right{
    			m := left+(right-left)/2
    			if ends[m] < nums[i]{
    				left = m+1
    			}else{
    				right = m-1
    			}
    		}
    		if left < len(ends) && ends[left] >= nums[i]{
    			ends[left] = nums[i]
    		}else{
    			ends = append(ends,nums[i])
    		}
    		dp[i] = left+1
    		res = MAX(res,dp[i])
    	}
    	return res
    }
    

      

  • 相关阅读:
    常用sql
    简单的Highcharts图表
    Js获取当前日期时间及其它操作
    js数组方法详解(最新最全)
    数组循环的六种方法
    js异步回调Async/Await与Promise区别
    【javascript】函数中的this的四种绑定形式 — 大家准备好瓜子,我要讲故事啦~~
    NPM小结
    WebSocket协议:5分钟从入门到精通
    HTTPS简单了解
  • 原文地址:https://www.cnblogs.com/wsw-seu/p/12755185.html
Copyright © 2020-2023  润新知