朴素算法

不必多说,\(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. golang之copier

    今天我们要介绍的copier库就能处理不同类型之间的赋值.除此之外,copier还能: 调用同名方法为字段赋值: 以源对象字段为参数调用目标对象的方法,从而为目标对象赋值(当然也可以做其它的任何事情) ...

  2. 4-CSRF漏洞渗透与防御

    1.什么是CSRF漏洞 Cross-Site Request Forgery 跨站请求伪造 从一个第三方的网站,利用其他网站生效的cookie,直接请求服务器的某一个接口,导致攻击发生! 2.CSRF ...

  3. 使用 fiddler 进行抓包处理

    1.概述 fiddler是一个抓包工具,有时候方便我们在访问网页上,看看网页的参数和返回结果.其中很重要的一条是,可以查看网页的响应速度,在对于调优方面提供一些依据. 2.软件安装 我们可以通过360 ...

  4. 【kernel】从 /proc/sys/net/ipv4/ip_forward 参数看如何玩转 procfs 内核参数

    本文的开篇,我们先从 sysctl 这个命令开始. sysctl 使用 sysctl 是一个 Linux 系统工具,后台实际上是 syscall,它允许用户查看和动态修改内核参数. # 查看当前设置的 ...

  5. vtkCellLocator IntersectWithLine 返回不是最近的交点

    vtkCellLocator IntersectWithLine 有一个重载函数(下面),返回不是最近的交点,因为到交点的距离没有比较,就直接覆盖了.不知道原本是否就是这样.可以用其他重载代替. in ...

  6. Chats 开发指南

    Chats 开发指南 欢迎使用 Chats!在我上一篇博客 https://www.cnblogs.com/sdcb/p/18597030/sdcb-chats-intro 中,我介绍了 Chats ...

  7. 【Amadeus原创】Docker容器的备份与还原

    主要作用: 就是让配置好的容器,可以得到复用,后面用到得的时候就不需要重新配置. 其中涉及到的命令有: docker commit 将容器保存为镜像 docker save -o 将镜像备份为tar文 ...

  8. T语言开发笔记1

    为什么会有开发语言的想法 在2012年,我准备开发一个给前端切图使用的屏幕取色器. 需求很简单,前端经常需要获取设计稿特定位置的颜色代码.虽然当时 PhotoShop 提供了内部取色器,但操作麻烦,而 ...

  9. Spring AOP实例操作 简单易懂

    AOP的功能,不改变源代码可以增强类中的方法      (增强 = 代理) AOP切入点表达式: execution([权限修饰符] [返回值类型] [类全路径] [方法名称] ([参数列表])) 例 ...

  10. 【报错解决】【Vue】与后端交互时,http与https跨域问题

    问题 xhr.js:220 Mixed Content: The page at 'https://xxx' was loaded over HTTPS, but requested an insec ...