提供一个好题:
题目意思:给定一个数列 ,问:
- 它的最长不上升子序列长度;(不上升和上升是有区别的)
- 最少能被划分成多少个不上升子序列。
LIS(最长上升子序列)模板:(以单调递增,最长上升序列中没有相同的数为例)
贪心思想!!!
- low数组的每一个下标都是一个栈为存储递减序列,栈顶(low数组下标下的数)
- 当遍历到的数t大于栈顶 ,开创下一个栈,并存入。(数更小时时,要放到栈顶,覆盖之前的数,贪心思想:前边数越小,后边存的数才可能越多!!)
- 当要存入的数t小于当前栈顶,搜索找到所有已经开的栈顶的数大于t的第一个栈中(栈顶的数在所有的栈中是单调递增的),这个操作的理解有难度,以一张图体会其作用:
二分搜索优化后为O(nlogn)
const int maxn =300003, INF = 0x7f7f7f7f;
int low[maxn], a[maxn];
int n, ans;
int binary_search(int *a, int R, int x)
//二分查找,返回a数组中第一个>=x的位置
{
int L = 1, mid;
while(L <= R)
{
mid = (L+R) >> 1;
if(a[mid] <= x)
L = mid + 1;
else
R = mid - 1;
}
return L;
}
int main()
{
scanf("%d", &n);
for(int i=1; i<=n; i++)
{
scanf("%d", &a[i]);
low[i] = INF; //由于low中存的是最小值,所以low初始化为INF
}
low[1] = a[1];
ans = 1; //初始时LIS长度为1
for(int i=2; i<=n; i++)
{
if(a[i] > low[ans]) //若a[i]>=low[ans],直接把a[i]接到后面
low[++ans] = a[i];
else //否则,找到low中第一个>=a[i]的位置low[j],用a[i]更新low[j]
low[binary_search(low, ans, a[i])] = a[i];
}
printf("%d\n", ans); //输出答案
return 0;
}
例题源代码 :
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stack>
#include <stdlib.h>
#include <iostream>
#include <math.h>
#include <queue>
#include <algorithm>
#define N 3000050
using namespace std;
int high[100010] = { 0 };
int low[100010] = { 0 };
int num = 1;
int search(int i,int j,int x) {
int l = i, r = j;
while (l <= r) {
int mid = (l + r) >> 1;
if (high[mid] >= x)
l= mid+1;
else r= mid-1 ;
}
return l;
}
int search2(int i, int j, int x) {
int l = i, r = j;
while (l <= r) {
int mid = (l + r) >> 1;
if (low[mid] <x)
l = mid + 1;
else r = mid - 1;
}
return r+1;
}
int main() {
int t[100010];
int n = 1;
while (scanf("%d", &t[n])!=EOF) {
n++;
}
int len = 1;
high[len] = t[1];
for (int i = 2; i <= n-1; i++) {
if (high[len] >= t[i])high[++len] = t[i];
else high[search(1,len,t[i])] = t[i];
}
printf("%d\n", len);
low[num] = t[1];
for (int i = 2; i <= n - 1; i++) {
if (low[num] < t[i])low[++num] = t[i];
else low[search2(1, num, t[i])]=t[i];
}
printf("%d", num);
return 0;
}