题解

最长非降子序列问题描述: 给若干个数,输出这些数能组成的最长非降子序列的长度。如2,4,6,3,4,5,6的最长非降子序列为2,3,4,5,6。所以最长非降子序列长度为5。
最长非降子序列问题经典解法--动态规划DP--时间复杂度O(nlogn)

input的数 能组成的非降子序列 非降子序列最后一个数(位置) 最长非降子序列 最长非降子序列长度
2 2 2(1) 2 1
4 2,4 4(2) 2,4 2
6 2,4,6 6(3) 2,4,6 3
3 2,4,6/2,3 6(3)/3(2) 2,4,6 3
4 2,4,6/2,3,4 6(3)/4(3) 2,4,6/2,3,4 3
5 2,4,6/2,3,4,5 6(3)/5(4) 2,3,4,5 4
6 2,4,6,6/2,3,4,5,6 6(4)/6(5) 2,3,4,5,6 5

可以看出,每个非降子序列只有最后一个数的大小和位置(代表了该非降子序列的长度)至关重要!而非降子序列的其他数不太重要。通过观察可以用一个辅助数组dp存非降子序列相关信息,在input一个数num时
1)如果num大于等于dp最后一个元素,为了得到更长的非降子序列,应该将num直接添加到dp的最后
2)如果num不大于dp的最后一个元素,说明这个数无法加入当前最长非降子序列,但是这个数作为其他子序列的最后一个数,应该存下来,所以将dp中第一个大于num的数替换为num(就算只是dp的最后一个元素大于num,这个时候意味着num作为最后一个数的序列和当前最长非降子序列一样长,这种情况也应该替换,因为num更小,组成的非降子序列更紧凑,在后面更有潜力找到更长的非降子序列)

input的数 能组成的非降子序列 dp辅助数组 最长非降子序列 最长非降子序列长度
2 2 2 2 1
4 2,4 2,4 2,4 2
6 2,4,6 2,4,6 2,4,6 3
3 2,4,6/2,3 2,3,6 2,4,6 3
4 2,4,6/2,3,4 2,3,4 2,4,6/2,3,4 3
5 2,4,6/2,3,4,5 2,3,4,5 2,3,4,5 4
6 2,4,6,6/2,3,4,5,6 2,3,4,5,6 2,3,4,5,6 5

这样dp的长度就是最长非降子序列的长度

本题中因为长方形有长和宽两个参数,则先对长方形的宽w进行由小到大排序,从而将本题简化为对长l求最长非降子序列。

【注】调用库函数:ForwardIter lower_bound(ForwardIter first, ForwardIter last,const _Tp& val)算法返回一个非递减序列[first, last)中的第一个大于等于值val的位置。

#include<bits/stdc++.h>
using namespace std;
#define N 1000000

//定义矩形数组  
struct rectangle {
    int w = 0, l = 0;
} a[N];
int dp[N];

//矩形数组排序方式:先根据矩形宽从小到大排序,宽相同时按长从小到大排序 
bool cmp(rectangle x, rectangle y){
    return x.w == y.w ? x.l < y.l : x.w < y.w;
}

int main()
{
//输入矩形数据到数组中 
    int n;
    cin >> n;
    for(int i = 0; i < n; i++) {
        cin >> a[i].w >> a[i].l;
    }
//对矩形数组进行排序 
    sort(a, a + n, cmp);
//求最长上升子序列的长度
    dp[0] = a[0].l;
    int len = 1;
    for(int i = 1; i < n; i++) {
        if(a[i].l >= dp[len-1]) {
            dp[len++] = a[i].l;
        } else {
            *(upper_bound(dp, dp + len, a[i].l)) = a[i].l;
        }
    }
    cout << len << endl;
    return 0;
}