最长不降子序列 n log n 方案输出与 Dilworth 定理 - 动态规划模板
朴素算法
不必多说,\(O(n^2)\) 的暴力 dp 转移。
优化算法
时间为 \(O(n \log n)\) ,本质是贪心,不是 dp 。
思路是维护一个单调栈(手写版),使这个栈单调不降。
- 当该元素 \(\ge\) 栈顶元素时,把这个元素压入栈中。
- 否则,在单调栈中找到第一个大于该元素的项,把这一项改为这个元素。(因为要使每个元素尽可能小,才有更大的拓展空间)。这个过程可以用二分实现,推荐使用码量更小的 upper_bound 或 lower_bound 。
最后,单调栈里元素个数即为最长不降子序列的长度。
#include <bits/stdc++.h>
using namespace std;
int n,a[100005],sn[100005],tp=0;
int main()
{
sn[0]=-0x3f3f3f3f;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
if(a[i]>=sn[tp])
{
sn[++tp]=a[i];
}
else
{
int tmp=upper_bound(sn+1,sn+tp+1,a[i])-sn;
sn[tmp]=a[i];
}
}
cout<<tp<<endl;
return 0;
}
输出方案
这样的贪心看似会破坏最长不降子序列的结构,不能输出;实际上我们可以通过记录一条链的方式输出正确的方案。
我们可以同时记录下单调栈里每个元素在原序列中的下标。
- 对于直接压入栈顶的元素,我们把它在链表中的前驱设为它压入之前的栈顶元素。
- 对于二分修改的元素,我们把它在链表中的前驱设为目前单调栈中它所处的位置的前一个元素。(不能设为被修改元素的前驱,因为这个前驱可能不是修改之后的。)
最后从栈顶的元素开始,遍历其前驱,记录在 vector 中,最后倒序输出即可。
#include <bits/stdc++.h>
using namespace std;
int n,a[100005],pre[100005],si[100005],sn[100005],tp=0;
vector<int>v;
int main()
{
sn[0]=-0x3f3f3f3f;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
if(a[i]>=sn[tp])
{
pre[i]=si[tp];
si[++tp]=i;
sn[tp]=a[i];
}
else
{
int tmp=upper_bound(sn+1,sn+tp+1,a[i])-sn;
pre[i]=si[tmp-1];
si[tmp]=i;
sn[tmp]=a[i];
}
}
cout<<tp<<endl;
int now=si[tp];
while(now)
{
v.push_back(a[now]);
now=pre[now];
}
reverse(v.begin(),v.end());
for(auto x:v)cout<<x<<' ';
return 0;
}
Dilworth 定理
挺难理解的,目前只能死记,或者通过贪心理解。
例题:导弹拦截第二问。
Dilworth 定理:对于任何有限偏序集,其最大反链中元素的数目等于最小链覆盖中链的数目。
在导弹拦截中,“最小链覆盖” 就相当于是求最少要用多少导弹系统,而 “最大反链” 就相当于是本问的做法:**求最长上升子序列。(因为最小链指最长不升子序列,那么反链即为最长上升子序列)
导弹拦截-不记录方案版
#include <bits/stdc++.h>
using namespace std;
int n=1,h[100005],sn[100005],tp=0,un[100005];
bool cmp(const int &val,const int &e)
{
return e<val;
}
int main()
{
while(cin>>h[n])n++;
n--;
sn[0]=0x3f3f3f3f;
for(int i=1;i<=n;i++)
{
if(sn[tp]>=h[i])
{
sn[++tp]=h[i];
}
else
{
int tmp=upper_bound(sn+1,sn+tp+1,h[i],cmp)-sn;
sn[tmp]=h[i];
}
}
cout<<tp<<endl;
tp=0;
for(int i=1;i<=n;i++)
{
if(un[tp]<h[i])
{
un[++tp]=h[i];
}
else
{
int tmp=lower_bound(un+1,un+tp+1,h[i])-un;
un[tmp]=h[i];
}
}
cout<<tp<<endl;
return 0;
}
导弹拦截-记录方案版
#include <bits/stdc++.h>
using namespace std;
int n=1,h[100005],si[100005],sn[100005],tp=0,ui[100005],un[100005],pre[100005];
vector<int>v;
bool cmp(const int &val,const int &e)
{
return e<val;
}
int main()
{
while(cin>>h[n])n++;
n--;
sn[0]=0x3f3f3f3f;
for(int i=1;i<=n;i++)
{
if(sn[tp]>=h[i])
{
pre[i]=si[tp];
sn[++tp]=h[i];
si[tp]=i;
}
else
{
int tmp=upper_bound(sn+1,sn+tp+1,h[i],cmp)-sn;
pre[i]=si[tmp-1];
sn[tmp]=h[i];
si[tmp]=i;
}
}
cout<<tp<<endl;
int now=si[tp];
while(now)
{
v.push_back(h[now]);
now=pre[now];
}
reverse(v.begin(),v.end());
for(auto x:v)cout<<x<<' ';
cout<<endl;
tp=0;
for(int i=1;i<=n;i++)
{
if(un[tp]<h[i])
{
un[++tp]=h[i];
ui[tp]=i;
}
else
{
int tmp=lower_bound(un+1,un+tp+1,h[i])-un;
un[tmp]=h[i];
ui[tmp]=i;
}
}
cout<<tp<<endl;
return 0;
}
以第 \(i\) 个数为结尾的最长上升子序列
例题:合唱队形 。
直接把二分时求到的要修改的数的下标便是以它为结尾的最长上升子序列的长度。
因为它后面的是比他高的,前面是比他矮的,并且满足以它为结尾。
#include <bits/stdc++.h>
using namespace std;
int n,h[105],up[105],down[105],ans=0,sn[105],tp=0;
int main()
{
cin>>n;
for(int i=1;i<=n;i++)cin>>h[i];
sn[0]=-0x3f3f3f3f;
for(int i=1;i<=n;i++)
{
int tmp=lower_bound(sn+1,sn+tp+1,h[i])-sn;
up[i]=tmp;
if(tmp>tp)sn[++tp]=h[i];
else sn[tmp]=h[i];
}
tp=0;
sn[0]=-0x3f3f3f3f;
for(int i=n;i>=1;i--)
{
int tmp=lower_bound(sn+1,sn+tp+1,h[i])-sn;
down[i]=tmp;
if(tmp>tp)sn[++tp]=h[i];
else sn[tmp]=h[i];
}
for(int i=1;i<=n;i++)
{
ans=max(ans,up[i]+down[i]-1);
}
cout<<n-ans;
return 0;
}
最长不降子序列 n log n 方案输出与 Dilworth 定理 - 动态规划模板的更多相关文章
- 最长非降子序列的O(n^2)解法
这次我们来讲解一个叫做"最长非下降子序列"的问题及他的O(n^2)解法. 首先我们来描述一下什么是"最长非下降子序列". 给你一个长度为n的数组a,在数组a中顺 ...
- HDU 1025-Constructing Roads In JGShining's Kingdom(最长不降子序列,线段树优化)
分析: 最长不降子序列,n很大o(n^2)肯定超,想到了小明序列那个题用线段树维护前面的最大值即可 该题也可用二分搜索来做. 注意问题输出时的坑,路复数后加s #include <map> ...
- Codeforces Round #198 (Div. 2) D. Bubble Sort Graph (转化为最长非降子序列)
D. Bubble Sort Graph time limit per test 1 second memory limit per test 256 megabytes input standard ...
- P1020 导弹拦截 (贪心+最长不降子序列)
题目描述 某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统.但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度.某天,雷达捕捉到敌国的导弹 ...
- (最长不降子序列)最少拦截系统 -- hdu -- 1257
http://acm.hdu.edu.cn/showproblem.php?pid=1257 最少拦截系统 Time Limit: 2000/1000 MS (Java/Others) Memo ...
- DP:凑零钱问题/最长非降子序列(C++)
你给出一定数额的钱 i 元给我,我利用手中的硬币(m元, j元, k元...)兑换等值的钱给你,要求硬币数最少. 举例:给出1-11的钱,手中硬币有1元,3元,5元. 重点是找到状态和状态转移方程. ...
- hdu6537 /// DP 最长不降子序列->最长公共子序列
题目大意: 给定一个字符串 字符为0~9 求翻转某个区间后使得串中的最长不降子序列最长 因为字符范围为0~9 假设有一个 0 1 2 3 4 5 6 7 8 9 的序列 此时翻转某个区间得到形如 0 ...
- 最长非降子序列的N*logN解法
之前讲到过求最长非降子序列的O(N^2)解法. 链接 这次在原来的基础上介绍一下N*logN解法. 该解法主要是维护一个数组minValue,minValue[i]表示最长上身子序列长度为i的数的最小 ...
- bzoj1609 / P2896 [USACO08FEB]一起吃饭Eating Together(最长不降子序列)
P2896 [USACO08FEB]一起吃饭Eating Together 显然的最长不升/降子序列,求出最长值,则答案为$n-$最长值(改掉剩下的). 复杂度$O(nlogn)$ (然鹅有神仙写了$ ...
- BZOJ 1046 最长不降子序列(nlogn)
nlogn的做法就是记录了在这之前每个长度的序列的最后一项的位置,这个位置是该长度下最后一个数最小的位置.显然能够达到最优. BZOJ 1046中里要按照字典序输出序列,按照坐标的字典序,那么我萌可以 ...
随机推荐
- Jetson Orin NX烧录+设备树更改?看这一篇就够了!
Jetson Orin NX烧录+设备树更改?看这一篇就够了! 笔者的设备为Jetson Orin NX 16GB + 达妙科技的Orin NX载板 本博客同步发表在CSDN:https://blog ...
- vite2+vue3使用tsx报错React is not defined、h is not defined
vite 为 .jsx 和 .tsx 文件提供开箱即用支持. 如果不是在 react 中使用 jsx,对于报错: React is not defined 需要在 vite.config.js 文件中 ...
- Windows安装redis并将redis设置成服务开机自启
Redis 作为一种缓存工具,主要用于解决高并发的问题,在分布式系统中有着极其广泛的应用,Redis 本身是应用于 Linux/Unix 平台的(部署在服务器上边),官方并没有提供 Windows 平 ...
- StarBlog博客Vue前端开发笔记:(2)页面路由
前言 Vue.js 使用虚拟 DOM 处理单页面,然后使用 Webpack 打包.通过上一篇文章的例子,读者也许已经发现,无论语法和写法如何不同,Vue.js 程序打包后都是一个单一的 HTML 文件 ...
- ESP32网页示波器+波形发生器
项目开源地址:https://github.com/guohaomeng/ESP32WebScope ESP32WebScope 只用一块ESP32制作的ESP32网页示波器+波形发生器,可以拿来生成 ...
- COSBrowser iOS 版 | 如何不打开 App 查看监控数据?
您是否有遇到这样的场景?当需要实时查看存储监控数据.查看某个存储桶的对象数量,又或者想了解某一个存储类型文件的下载量在当前与前一天的对比情况,是上涨了还是下降了,这时您是否也在经历频繁的打开关闭 Ap ...
- js 进制转换:十六进制转十进制、十进制转十六进制、十六进制转ASCII码、
因为近期做小程序,蓝牙连接硬件,需要根据module bus通信协议解析数据,用到了很多标题的算法转换,借此总结一下. 十六进制 转 十进制 function hex2dec(hex) { var l ...
- 好消息,在 Visual Studio 中可以免费使用 GitHub Copilot 了!
前言 今天大姚给大家分享一个好消息,GitHub Copilot 可以免费使用了!在此之前若开发者要使用 GitHub Copilot 需要付费订阅,每月订阅费用起步价为 10 美元,而经过验证的学生 ...
- k8s备份恢复实践--velero
k8s备份恢复实践--velero 使用Velero备份k8资源到minio,阿里云oss,七牛云Kodo 环境linux + kubectl #1.velero简介 Velero是用于Kuberne ...
- 对DenseTensor进行Transpose
ML.NET 是微软推出的为. NET 平台设计的深度学习库,通过这个东西(ModelBuilder)可以自己构建模型,并用于后来的推理与数据处理.虽然设计是很好的,但是由于现在的 AI 发展基本上都 ...