最长上升子序列(LIS)nlogn模板

参考https://www.cnblogs.com/yuelian/p/8745807.html

注意最长上升子序列用lower_bound,最长不下降子序列用upper_bound
比如123458, 加入了5
假设求最长上升子序列
这个时候只能替换5,不能替换8(严格上升)
虽然没有用,但是这样不会错,写upper_bound就错了。
假设求最长不下降子序列
这样应该替换8,替换5并不是最优的
所以用upper_bound

 

最长上升子序列(LIS)nlogn模板

#include<cstdio>
#include<algorithm>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
using namespace std;

const int MAXN = 51234;
int a[MAXN], f[MAXN], n; //a数组从0开始,f数组从1开始 

int main()
{
	scanf("%d", &n);
	REP(i, 0, n) scanf("%d", &a[i]);
	int len = 1;
	f[1] = a[0]; //初始化 
	REP(i, 1, n)
	{
		if(a[i] > f[len]) f[++len] = a[i]; //这里是++len 若是不下降就改为>= 
		else f[lower_bound(f + 1, f + len + 1, a[i]) - f] = a[i]; //注意f数组是从1开始 
	}
	printf("%d\n", len);
	return 0;
}

 

最长不下降子序列(LIS)nlogn模板

#include<cstdio>
#include<algorithm>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
using namespace std;

const int MAXN = 51234;
int a[MAXN], f[MAXN], n; 

int main()
{
	scanf("%d", &n);
	REP(i, 0, n) scanf("%d", &a[i]);
	int len = 1;
	f[1] = a[0]; 
	REP(i, 1, n)
	{
		if(a[i] >= f[len]) f[++len] = a[i]; //>改成>= 
		else f[upper_bound(f + 1, f + len + 1, a[i]) - f] = a[i];  //lower_bound改成upper_bound
	}
	printf("%d\n", len);
	return 0;
}

 

如果要求最长下降子序列或者最长不上升子序列符号改变,同时二分加上cmp即可

另外有个神奇的定理
如果是求一个数组最少分成几组最长不上升子序列的话
答案就是最长上升子序列(上升改成下降也成立)
导弹拦截那题要用到

 

输出路径的版本,见https://blog.csdn.net/lxcxingc/article/details/81238008

#include<cstdio>
#include<algorithm>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
using namespace std;

const int MAXN = 51234;
int a[MAXN], f[MAXN];
int ans[MAXN], pos[MAXN], n;

int main()
{
	scanf("%d", &n);
	REP(i, 0, n) scanf("%d", &a[i]);
	int len = 1;
	f[1] = pos[1] = a[0];
	REP(i, 1, n)
	{
		if(a[i] > f[len]) f[++len] = a[i], pos[i] = len;
		else f[pos[i] = lower_bound(f + 1, f + len + 1, a[i]) - f] = a[i];
	}
	printf("%d\n", len);
	
	int maxx = 1e9, t = len;
	for(int i = n - 1; i >= 0; i--)
	{
		if(t == 0) break;
		if(pos[i] == t && maxx > a[i])
		{
			maxx = a[i];
			ans[t--] = a[i];
		}
	}
	REP(i, 1, len + 1) printf("%d ", ans[i]);
	puts("");
	
	return 0;	
} 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值