俄罗斯套娃信封问题(LeetCode 354)
给定一些标记了宽度和高度的信封,宽度和高度以整数对形式 (w, h)
出现。当另一个信封的宽度和高度都比这个信封大的时候,这个信封就可以放进另一个信封里,如同俄罗斯套娃一样。
请计算最多能有多少个信封能组成一组“俄罗斯套娃”信封(即可以把一个信封放到另一个信封里面)。
说明:
不允许旋转信封。
示例:
输入: envelopes = [[5,4],[6,4],[6,7],[2,3]]
输出: 3
解释: 最多信封的个数为 3, 组合为: [2,3] => [5,4] => [6,7]。
-
暴力的动态规划方法(可能超时)
public int maxEnvelopes(int[][] envelopes) { int[] temp; int res1 = 1; //按宽度从小到大排序,相同宽度时高度从小到大排序 for(int i = 0;i<envelopes.length;i++){ for(int j = i+1;j<envelopes.length;j++){ if(envelopes[i][0]>envelopes[j][0] || (envelopes[i][0]==envelopes[j][0] && envelopes[i][1]>envelopes[j][1])){ temp = envelopes[i]; envelopes[i] = envelopes[j]; envelopes[j] = temp; } } } //对于每个信封,它能塞进的信封都只可能出现在它右侧 //res记录以第i个信封为最后一个(套娃中最小的套娃),它的“俄罗斯套娃”信封个数。例如长宽最大的信封,res[i]=1 //从右往左遍历,对于第i个信封,遍历其i+1至最后一个信封。 //如果当前信封的长和宽都比第i个信封的大,那么更新res数组: //res[i] = Math.max(res[i],res[j]+1); int[] res = new int[envelopes.length]; for(int i = envelopes.length-1;i>=0;i--){ res[i] = 1; for (int j = i+1;j<envelopes.length;j++){ if(envelopes[i][1] < envelopes[j][1] && envelopes[i][0] < envelopes[j][0]){ res[i] = Math.max(res[i],res[j]+1); } } resnum = Math.max(resnum,res[i]); } return resnum; }
-
转换为求最长递增子序列问题
我们将宽度由小到大排序,同样宽度的信封要求高度由大到小。
高度所组成的数组,求最长的上升子序列,其值就是信封的最大值。
个人的一些理解:假设高度所组成的数组中有个高度h,如果它之前有一个比h还小的高度h0,就可以吧子序列长度+1(信封问题中就表示可以把h0塞到h信封中)。因为h的宽度一定比h0大,这已经事先排序完成了。
public int maxEnvelopes(int[][] envelopes) { if(envelopes.length<=1) return envelopes.length; int[] temp; //排序:先排宽度,从小到大;对于相同的宽度,高度从大到小。 for(int i = 0;i<envelopes.length;i++){ for(int j = i+1;j<envelopes.length;j++){ if(envelopes[i][0]>envelopes[j][0] || (envelopes[i][0]==envelopes[j][0] && envelopes[i][1]<envelopes[j][1])){ temp = envelopes[i]; envelopes[i] = envelopes[j]; envelopes[j] = temp; } } } //对于排序完之后的高度h,求最长上升子序列 int res = 0; int[] dp = new int[envelopes.length]; for(int i = 0;i<envelopes.length;i++){ dp[i] = 1; for(int j = 0;j<i;j++){ if(envelopes[i][1] > envelopes[j][1]){ dp[i] = Math.max(dp[i],dp[j]+1); } } } //返回dp中的最大值即可 for(int i:dp){ if(res<i) res=i; } return res; }