给你一个二维整数数组 envelopes ,其中 envelopes[i] = [wi, hi] ,表示第 i 个信封的宽度和高度。
当另一个信封的宽度和高度都比这个信封大的时候,这个信封就可以放进另一个信封里,如同俄罗斯套娃一样。
请计算 最多能有多少个 信封能组成一组“俄罗斯套娃”信封(即可以把一个信封放到另一个信封里面)。
注意:不允许旋转信封。
示例 1:
输入:envelopes = [[5,4],[6,4],[6,7],[2,3]]
输出:3
解释:最多信封的个数为 3, 组合为: [2,3] => [5,4] => [6,7]。
示例 2:
输入:envelopes = [[1,1],[1,1],[1,1]]
输出:1
其实问的是,最多能有几个完全覆盖的区间,这个和区间是不一样的概念,这个就是套娃,宽和高都要小;而区间嵌套则是a1<a2<b2<b1
首先,排序之后肯定能保证有一条边是满足的,关键在于另一个;
这个有点像暴力判断,最终要判断很多次
class Solution {
public int maxEnvelopes(int[][] es) {
int n = es.length;
if(n == 0) return n;
Arrays.sort(es, (a, b)->a[0] - b[0]);//一种简易的写法
int[] f = new int[n];//作为dp数组
int ans = 1;
for(int i = 0; i < n; i++){
f[i] = 1;//默认肯定是1
for(int j = i - 1; j >= 0; j--){这里的正序,逆序都是可以的
//依次遍历前面的
if(check(es, j, i)){
//判断对应j与i对应的数组,是否满足套娃
f[i] = Math.max(f[i], f[j] + 1);
//这里就体现出来依赖关系了,继承原有的结果再加1即可
//有必要求最大值,因为f[i]是在不断变动,所以需要比较
}
ans = Math.max(ans, f[i]);//ans并不是最后的ans[len】
}
}
return ans;
}
boolean check(int[][] es, int i, int j){
return es[i][0] < es[j][0] && es[i][1] < es[j][1];
}
}
2.其实可以对该形式进行简化,在check判断的时候,没必要判断左右,因为已经排过顺序了
只需要排序的时候再细化一下即可:
若左边相同,则右边从大到小即可,
咱们一点点优化
class Solution {
public int maxEnvelopes(int[][] es) {
int n = es.length;
if(n == 0) return n;
Arrays.sort(es, new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
return o1[0] - o2[0];
}
});
int ans = 1;
int dp[] = new int[n];
for(int i = 0; i < n; i++){
dp[i] = 1;
for(int j = 0; j < i; j++){
if(es[j][0] < es[i][0] && es[j][1] < es[i][1]){
dp[i] = Math.max(dp[i], dp[j] + 1);
}
ans = Math.max(ans, dp[i]);
}
}
return ans;
}
}
这里我只是取消了函数,放在了里面
class Solution {
public int maxEnvelopes(int[][] es) {
int n = es.length;
if(n == 0) return n;
Arrays.sort(es, new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
if(o1[0] != o2[0]){
return o1[0] - o2[0];
}else {
return o2[1] - o1[1];//逆序
}
}
});
int ans = 1;
int dp[] = new int[n];
for(int i = 0; i < n; i++){
dp[i] = 1;
for(int j = 0; j < i; j++){
if(es[j][1] < es[i][1]){
//这里我直接比较第二维即可
dp[i] = Math.max(dp[i], dp[j] + 1);
//如[4,5]与[4,6]排序之后就为[4,6]与[4,5]这样比较的时候就不会计算在内
}
ans = Math.max(ans, dp[i]);
}
}
return ans;
}
}
这里我多排序了一下,判断的时候少了一个条件,结果速度提升很快
改变递推方式
这题之前做过,有过相似的题型
class Solution {
public int maxEnvelopes(int[][] es) {
int n = es.length;
if(n == 0) return n;
Arrays.sort(es, new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
if(o1[0] != o2[0]){
return o1[0] - o2[0];
}else {
return o2[1] - o1[1];//逆序
}
}
});
ArrayList<Integer> dp = new ArrayList<>();
//dp用来存放最长子序列的,随时准备扩充队伍
//由于总体长度位置,所以这里选用list
dp.add(es[0][1]);
for(int i = 1; i < n; i++){
int num = es[i][1];
if(num > dp.get(dp.size() - 1)){
//这个时候可以加入
dp.add(num);
}else{
//一旦发现更小的,这个时候就需要更新里面的值
//如何定位,则选用二分查找法,第一个大于等于该值的位置
int index = binarySearch(dp, num);
dp.set(index, num);
}
}
return dp.size();
}
int binarySearch(List<Integer> list, int num){
int left = 0, right = list.size() - 1;
while(left < right){
int mid = (left + right) / 2;
if(list.get(mid) < num){
left = mid + 1;
}else{
right = mid;
}
}
return left;
}
int binarySearch(List<Integer> list, int num){//也可以这样写,比较熟悉的写法
int left = 0, right = list.size() - 1;
while(left < right){
int mid = (left + right) / 2;
if(list.get(mid) >= num){
right = mid;
}else{
left = mid + 1;
}
}
return left;
}
}