最长不降子序列 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中里要按照字典序输出序列,按照坐标的字典序,那么我萌可以 ...
随机推荐
- vagrant搭建开发环境
一:我们为什么需要用这玩意 我们在开发中经常会面临的问题:环境不一致,有人用Mac有人用Windos还有几个用linux的,而我们的服务器都是linux. 在我本地是可以的啊,我测了都,没有问题啊,然 ...
- Mybatis【12】-- Mybatis多条件怎么查询?
很多时候,我们需要传入多个参数给sql语句接收,但是如果这些参数整体不是一个对象,那么我们应该怎么做呢?这里有两种解决方案,仅供参考. 1.将多个参数封装成为Map 测试接口,我们传入一个Map,里面 ...
- nvm安装node报错Get "https://nodejs.org/dist/latest/SHASUMS256.txt": dial tcp 104.20.23.46:443: i/o timeout
windows上通过nvm管理node版本,在本地安装了nvm后,通过nvm安装node,报错了,信息: Could not retrieve https://nodejs.org/dist/late ...
- Vue CLI中views和components文件夹的区别
首先,src/components和文件夹src/views都包含Vue组件. 关键区别在于某些Vue组件充当路由视图. 在Vue中(通常是Vue Router)处理路由时,将定义路由以切换组件中使用 ...
- 探索 Spring AOP:全面解析与实战应用
在现代 Java 开发领域,Spring 框架无疑占据着重要地位,而 Spring AOP(Aspect-Oriented Programming,面向切面编程)作为 Spring 框架的关键特性之一 ...
- git 基础操作 && 在"develop"分支修改动但不想提交切换到"master"分支(最后)
测试学习准备条件:一个目录,含不含有文件都可以,创建一个test.txt 1. 创建空仓库 git init 2. 修改过后,添加到本地仓库 添加指定的文件,如果一个以上,文件名空格隔开 git ad ...
- 8.mysql表分区
MySQL表分区 表分区是将⼀个表的数据按照⼀定的规则⽔平划分为不同的逻辑块,并分别进⾏物理存储,这个规则就叫做分区函数,可以有不同的分区规则 5.7可以通过show plugins语句查看当前MyS ...
- Advanced .NET Remoting: 第 8 章 创建连接器
第 8 章 创建连接器 上一章向您展示了各种类型的连接器,以及它们对于请求的同步和异步处理过程.到目前为止,我一直忽略的一个最为重要的步骤是:初始化连接器和连接器链.连接器通常既不是直接待代码中创建, ...
- 中电资讯 - 一路“标”升,喜迎Q3开门红
Q2收获满满,Q3精彩再启! 近日,中电金信多项业务取得新进展 接连中标多个项目 中电金信数字科技集团旗下优智汇咨询中标浙江民泰商业银行信息科技战略发展规划(2022-2025)项目.优智汇将根据民泰 ...
- springboot 多数据源(aop方式)
一.实现思路 在yml中定义多个数据源的配置,然后创建一个类DynamicDataSource去继承AbstractRoutingDataSource类 AbstractRoutingDataSour ...