最长上升子序列的优化求法和Dilworth定理

最长上升子序列的优化求法和Dilworth定理

最长上升子序列

学过DP的都知道,求最长上升子序列的DP做法的时间复杂度是\(O(n^2)\)的,现在介绍一个\(O(n*\log n)\)的二分做法

二分做法

3 5 2 3 7 1一组原始数据,最长上升子序列的长度应该是3,为序列2 3 7
使用队列q,先把第一个元素放进去
q: 3
从第二个元素 x 开始只要满足最长上升子序列的条件就加在后面,不满足就在队列中找第一个比 x 大的元素替换 (用二分查找)
q: 3 5
q: 2 5
q: 2 3
q: 2 3 7
q: 1 3 7 这是最后一个状态,此时队列的长度即是最长上升子序列的长度,但是内容不是正确的那个最长上升子序列
  • 此做法只关注最后的长度,不关注内容是否正确

Dilworth定理

原链的最长长度=反链划分数的最小值

这是简单的总结,原始定义网上有很多

原链和反链?

不上升子序列 和 上升子序列互为反链

不下降子序列 和 下降子序列互为反链

链的划分数?

假设链是上升子序列,则按照上升子序列的规则对链进行划分,得到的满足规则的小链的数量即是链的划分数

重要例题

P1020 [NOIP1999 提高组] 导弹拦截

https://www.luogu.com.cn/problem/P1020

思路

第一问:根据题意很容易想到,就是一道求最长非上升子序列的问题,也很容易想到DP的做法

但是这道题会卡\(O(n^2)\)的做法,所以要采用二分法\(O(n * \log n)\)

第二问:第二问其实就是求不上升子序列的最小划分数,根据Dilworth定理,可以转化成求最长上升子序列的长度

代码

#include <bits/stdc++.h>

typedef std::pair<int, int> pii;
#define INF 0x3f3f3f3f
#define MOD 998244353
using i64 = long long;
const int N = 1e5+5;
int a[N];

void solve(){
	int n = 0;
	while (std::cin >> a[n]){
		n++;
	}
	std::vector<int> lnis, lis;
	lnis.push_back(a[0]);
	lis.push_back(a[0]);

	for (int i = 1; i < n; i++){
		if (*lnis.rbegin() >= a[i]){
			lnis.push_back(a[i]);
		}else{
			int k = std::upper_bound(lnis.begin(), lnis.end(), a[i], std::greater<int>()) - lnis.begin();
			lnis[k] = a[i];
		}

		if (*lis.rbegin() < a[i]){
			lis.push_back(a[i]);
		}else{
			int k = std::lower_bound(lis.begin(), lis.end(), a[i]) - lis.begin();
			lis[k] = a[i];
		}
	}

	std::cout << lnis.size() << '\n' << lis.size();

}

signed main()
{
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);
	std::cout<<std::setiosflags(std::ios::fixed)<<std::setprecision(2);
	int t = 1, i;
	for (i = 0; i < t; i++){
		solve();
	}
	return 0;
}
posted @ 2025-01-02 18:22  califeee  阅读(105)  评论(0)    收藏  举报