题解
最长非降子序列问题描述: 给若干个数,输出这些数能组成的最长非降子序列的长度。如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;
}

京公网安备 11010502036488号