朴素算法

不必多说,\(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 定理 - 动态规划模板的更多相关文章

  1. 最长非降子序列的O(n^2)解法

    这次我们来讲解一个叫做"最长非下降子序列"的问题及他的O(n^2)解法. 首先我们来描述一下什么是"最长非下降子序列". 给你一个长度为n的数组a,在数组a中顺 ...

  2. HDU 1025-Constructing Roads In JGShining's Kingdom(最长不降子序列,线段树优化)

    分析: 最长不降子序列,n很大o(n^2)肯定超,想到了小明序列那个题用线段树维护前面的最大值即可 该题也可用二分搜索来做. 注意问题输出时的坑,路复数后加s #include <map> ...

  3. 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 ...

  4. P1020 导弹拦截 (贪心+最长不降子序列)

    题目描述 某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统.但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度.某天,雷达捕捉到敌国的导弹 ...

  5. (最长不降子序列)最少拦截系统 -- hdu -- 1257

    http://acm.hdu.edu.cn/showproblem.php?pid=1257 最少拦截系统 Time Limit: 2000/1000 MS (Java/Others)    Memo ...

  6. DP:凑零钱问题/最长非降子序列(C++)

    你给出一定数额的钱 i 元给我,我利用手中的硬币(m元, j元, k元...)兑换等值的钱给你,要求硬币数最少. 举例:给出1-11的钱,手中硬币有1元,3元,5元. 重点是找到状态和状态转移方程. ...

  7. hdu6537 /// DP 最长不降子序列->最长公共子序列

    题目大意: 给定一个字符串 字符为0~9 求翻转某个区间后使得串中的最长不降子序列最长 因为字符范围为0~9 假设有一个 0 1 2 3 4 5 6 7 8 9 的序列 此时翻转某个区间得到形如 0 ...

  8. 最长非降子序列的N*logN解法

    之前讲到过求最长非降子序列的O(N^2)解法. 链接 这次在原来的基础上介绍一下N*logN解法. 该解法主要是维护一个数组minValue,minValue[i]表示最长上身子序列长度为i的数的最小 ...

  9. bzoj1609 / P2896 [USACO08FEB]一起吃饭Eating Together(最长不降子序列)

    P2896 [USACO08FEB]一起吃饭Eating Together 显然的最长不升/降子序列,求出最长值,则答案为$n-$最长值(改掉剩下的). 复杂度$O(nlogn)$ (然鹅有神仙写了$ ...

  10. BZOJ 1046 最长不降子序列(nlogn)

    nlogn的做法就是记录了在这之前每个长度的序列的最后一项的位置,这个位置是该长度下最后一个数最小的位置.显然能够达到最优. BZOJ 1046中里要按照字典序输出序列,按照坐标的字典序,那么我萌可以 ...

随机推荐

  1. laravel之验证器

    开发中使用框架自带验证器进行参数验证 1.定义验证器基类,定义失败返回值 新建基础类文件 app > Http > Requests > BaseRequest.php <?p ...

  2. php之编译安装

    1. 安装所需环境 yum -y install libxml2 libxml2-devel openssl openssl-devel curl-devel libjpeg-devel libpng ...

  3. Blazor 组件库 BootstrapBlazor 中Button组件介绍

    组件介绍 按钮组件,应该是最基础的组件之一了.感觉没什么可介绍的,但是BootstrapBlazor的按钮,还是有很多不错的特性. 首先是最基础的,boostrap5的按钮样式: 代码如下: < ...

  4. VTK 显示3D模型的网格线(线框/wireframe)

    在VTK9.1.0在Windows10+VS2019+Qt 5.15.2环境下编译安装的Qt例子中,想显示球体表面的网格线(线框/wireframe),设置actor的EdgeVisibilityOn ...

  5. Linux下TCP/IP编程--TCP实战

    之前尝试过windows下的简单TCP客户端服务器编写,这次尝试下一下Linux环境下的TCP 客户端代码 #include <stdio.h> #include <stdlib.h ...

  6. bridge模式下 所有容器的 网关 都是 宿主机docker0网卡的ip

    bridge模式下 所有容器的 网关 都是 宿主机docker0网卡的ip

  7. kubeadm init问题

    1.解析不到对应的主机 [WARNING Hostname]: hostname "k8s-master-01" could not be reached  [WARNING Ho ...

  8. 使用P6Spy监控你的Spring boot数据库操作

    引言 最近换了号称最快的HikariDataSource,由于没有了SQL监控,加之于Mybaits默认输出日志之拙计.遂用此物,与之相仿还有log4jdbc,比较活跃度后选择了P6Spy. 版本 P ...

  9. Qt编写地图综合应用2-迁徙图

    一.前言 在很多web系统中,尤其是大屏系统中,经常可以看到类似于飞机迁徙图的效果,这个在echart中也是最常用的一个效果,迁徙图既可以是一个飞机也可以是其他形状,然后有一条动态的移动轨迹来表示流向 ...

  10. Soulmate

    理想之所以是理想,也就是因为它只能存在于脑海中,天上月是天上月,水中花是水中花.但我们仍可以怀揣着对乌托邦的向往,所以,我对理想中的对象设想如下: 原来形容一个女子的眉眼,我总喜欢说眉眼如黛,眉如远山 ...