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

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

最长上升子序列

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

二分做法

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(n2)的做法,所以要采用二分法O(nlogn)

第二问:第二问其实就是求不上升子序列的最小划分数,根据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;
}

本文作者:califeee

本文链接:https://www.cnblogs.com/califeee/p/18648524

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   califeee  阅读(36)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
💬
评论
📌
收藏
💗
关注
👍
推荐
🚀
回顶
收起
  1. 1 404 not found REOL
404 not found - REOL
00:00 / 00:00
An audio error has occurred.