利用lower_bound()和upper_bound()解决最长严格(非)上升/下降子序列长度问题
一、lower_bound()与upper_bound()基操
lower_bound():STL,返回某一给定容器区间内第一个大于等于某给定数字x的数字的指针。
upper_bound():STL,返回某一给定容器区间内第一个大于某给定数字x的数字的指针。
以下均以lower_bound()为例
-
基本操作
int a[11] = {0,1,2,3,4,5,6,7,8,9,10}; int x = 5; int* p = lower_bound( x+1, x+10+1 , x ); //对于区间 [ a[1], a[10] ], 同 sort cout << p << endl; cout << *p << endl; cout << a[a-p] << endl; -
更改比较方式
void cmp( int a, int b ) { return a > b; } int* p = lower_bound( x+1, x+10+1, x, cmp ); //由大于等于改为小于等于或
int* p = lower_bound( x+1, x+10+1, x, greater<int>() ); //由大于等于改为小于等于
二、基本思想与证明
以下以最长上升子序列为例
我们以dp[len]表示数组a[len]中长度为len的上升子序列结尾的最小合法值。
当a[i]大于b[len]:将a[i]直接续在b[]数组之后。
当a[i]小于等于b[len]:在b[]中找到第一个大于等于a[i]的数,用a[i]替换它。
证明:
显然(证毕)。
当a[i]大于b[len],显然将a[i]直接续在b[]数组之后是合法的。
当a[i]小于等于b[len],则b[]中第一个大于等于a[i]的数一定不可能比a[i]优。(上升序列x越小x越能接)。
(证毕)
三、实操
#include <bits/stdc++.h>
using namespace std;
int a[1000], dp[1000],n ,len;
int main()
{
cin >> n;
for( int i = 1; i <= n; ++i )
cin >> a[i];
//------------------------------------------------
//最长上升子序列
dp[1] = a[1]; len = 1;
for( int i = 2; i <= n; ++i )
{
if( a[i] > dp[len] )
dp[++len] = a[i];
else
*lower_bound( dp+1, dp+len+1, a[i] ) = a[i];
}
cout << len << endl;
//------------------------------------------------
//最长下降子序列
dp[1] = a[1]; len = 1;
for( int i = 2; i <= n; ++i )
{
if( a[i] < dp[len] )
dp[++len] = a[i];
else
*lower_bound( dp+1, dp+len+1, a[i], greater<int>() ) = a[i];
}
cout << len << endl;
//------------------------------------------------
//最长不降子序列
dp[1] = a[1]; len = 1;
for( int i = 2; i <= n; ++i )
{
if( a[i] >= dp[len] )
dp[++len] = a[i];
else
*upper_bound( dp+1, dp+len+1, a[i] ) = a[i];
}
cout << len << endl;
//------------------------------------------------
//最长不升子序列
dp[1] = a[1]; len = 1;
for( int i = 2; i <= n; ++i )
{
if( a[i] <= dp[len] )
dp[++len] = a[i];
else
*upper_bound( dp+1, dp+len+1, a[i], greater<int>() ) = a[i];
}
cout << len << endl;
//------------------------------------------------
return 0;
}

浙公网安备 33010602011771号