最长上升子序列的优化求法和Dilworth定理
最长上升子序列的优化求法和Dilworth定理
最长上升子序列
学过DP的都知道,求最长上升子序列的DP做法的时间复杂度是
的,现在介绍一个 的二分做法
二分做法
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 提高组] 导弹拦截
思路
第一问:根据题意很容易想到,就是一道求最长非上升子序列的问题,也很容易想到DP的做法
但是这道题会卡
的做法,所以要采用二分法 第二问:第二问其实就是求不上升子序列的最小划分数,根据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 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步