◆模板·III◆ 单调子序列

以前只知道DP用 O(n2) 的做法,现在才发现求单调子序列方法好多……


◇ 模板简述

单调子序列包括 升序/降序/非升序/非降序 子序列。主要题型如下:

①在原串中找到一个最长的单调子序列;

②将原串分解为若干个单调子序列;

③通过修改元素使原串变为单调序列。

Tab: 子序列在原串中可以断开,也就是说若原串为A{a[1]~a[n]},则其子序列可以是 A'{a[b[1]]~a[b[n]]}满足 b[1]<b[2]<...<b[n]

方法的确很多,包括STL(lower_bound),DP……接着讲吧……


◇ 求原串中最长的单调子序列

【OpenJudge 1759】 +传送门+

一道非常经典的模板题——最长上升子序列。由于n最大1000,O(n2) 的算法是可以通过的。

解决这类问题的基础算法是DP。对于第i个数,我们可以找到一条以 i 结尾的上升子序列,记以 i 结尾的最长上升子序列的长度为 dp[i],也就是我们的DP状态。

如何转移?

对于第 i 个数,若存在第j个数(j<i)小于第i个数,则第i个数可以继续连接在以j结尾的最长上升子序列上,因此转移可以写成下列:

没有多大难度……其实只是数据规模不算大!

先附上代码(一个在 n≤3000 的情况下不会超时的版):

 /*Lucky_Glass*/
#include<cstdio>
#include<algorithm>
using namespace std;
int A[],dp[],n,ans=;
int DP(int x)
{
if(dp[x]) return dp[x];
dp[x]=; //只有 第i个元素 本身
for(int i=;i<x;i++)
if(A[i]<A[x])
dp[x]=max(dp[x],DP(i)+);
return dp[x];
}
int main()
{
scanf("%d",&n);
for(int i=;i<n;i++) scanf("%d",&A[i]);
dp[]=;
for(int i=n-;i>=;i--) //枚举对于以每一个元素为结尾的序列
ans=max(DP(i),ans);
printf("%d\n",ans);
return ;
}

难度↑【POJ 1631】Bridging signals +传送门+

只是数据规模上升到40000了……O(n2)算法会炸……我们需要一种比DP更优秀的算法(DP有时并不是最优算法)。

不难发现,我们有时会沿用上一个最长上升子序列的前部分(也就是小于等于当前元素的最大元素的后面)。所以我们可以把以当前元素结尾的最长上升子序列给存下来(seq[])。由于seq[]存储的是一个单调序列,所以我们可以在seq里二分查找(lower_bound)!

具体怎么做?

先初始化seq的所有值为正无穷(memset(seq,0x3f,sizeof seq)),这样才能在第一次查找时找到seq[1]。

若当前要求以num[i]结尾的最长上升子序列。先通过lower_bound找到seq[1~n-1]中小于等于num[i]的元素 seq[pos-1] (如果找不到此元素,则应找到seq[1]),然后将 seq[pos] 替换为num[i],此时seq[1~pos]则是以 num[i] 结尾的最长上升子序列,且序列长度为 pos,此时就可以更新答案了。

 /*Lucky_Glass*/
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=;
int num[MAXN+],seq[MAXN+];
int main()
{
int T;scanf("%d",&T);
while(T--)
{
int n;scanf("%d",&n);
for(int i=;i<n;i++) scanf("%d",&num[i]);
memset(seq,0x3f,sizeof seq); //初始化为正无穷
int ans=;
for(int i=;i<n;i++)
{
int pos=lower_bound(seq+,seq+n,num[i])-seq; //pos-1是小于等于num[i]的最大元素
seq[pos]=num[i]; //更新序列
ans=max(ans,pos); //更新答案
}
printf("%d\n",ans);
}
return ;
}

◇ 通过修改原串元素形成单调序列

【Codeforces 13C】Sequence +传送门+

将元素i从a改成b的花费为 |a-b| ,现给出一个序列,修改一些元素,使得序列形成单调序列(不下降/不上升),求最小花费。

一个简单结论:对于每一个数,修改其值为先前的数或不修改,一定可以使原串形成单调序列。

于是我们定义dp[i][j]为第i个数值为j时的最小花费。

从小到大枚举i先前的每一个数值(包括i,此时相当于不修改)j,表示第i个数将修改为j,则花费为|j-num[i]|。由于我们是从小到大枚举的值,我们可以储存Min,表示dp[i-1][1~j]中最小的值,以此更新dp[i][j],也就是说:

但是我们发现j太大了(1e9),存不下,于是可以用离散化来优化一下……最坏情况下,原序列每一个元素都不相等,第二维为5000,仍然存不下。回过头看我们的转移式,我们其实只需要用到dp[i-1],所以我们只需要滚动数组就可以了。

 /*Lucky_Glass*/
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<cmath>
using namespace std;
const int MAXN=;
typedef long long ll;
int n;
int hgt[MAXN+],fhgt[MAXN+];
vector<int> uni;
ll dp[][MAXN+];
int main()
{
scanf("%d",&n);
for(int i=;i<=n;i++)
scanf("%d",&hgt[i]),uni.push_back(hgt[i]);
sort(uni.begin(),uni.end());
uni.erase(unique(uni.begin(),uni.end()),uni.end()); //离散化
ll ans=ll(1e16);
for(int i=;i<=n;i++)
{
ll Min=ll(1e16);
for(int j=;j<uni.size();j++)
{
Min=min((ll)Min,dp[(i-)%][j]); //取1~j的最小值
dp[i%][j]=Min+fabs(1.0*hgt[i]-uni[j]); //%2 滚动数组
if(i==n) ans=min(ans,dp[i%][j]); //更新答案
}
}
printf("%lld\n",ans);
return ;
}

(Tab:以上每一篇代码都是模板,可以直接套上用的~)

The End

Thanks for reading!

- Lucky_Glass

(Tab:如果我有没讲清楚的地方可以直接在邮箱lucky_glass@foxmail.com email我,在周末我会尽量解答并完善博客~)

 

【模板时间】◆模板·III◆ 单调子序列的更多相关文章

  1. 洛谷 P1439 【模板】最长公共子序列

    \[传送门啦\] 题目描述 给出\(1-n\)的两个排列\(P1\)和\(P2\),求它们的最长公共子序列. 输入输出格式 输入格式: 第一行是一个数\(n\), 接下来两行,每行为\(n\)个数,为 ...

  2. P1439 【模板】最长公共子序列 LCS

    P1439 [模板]最长公共子序列 题解 1.RE的暴力DP O(n2) 我们设dp[i][j]表示,S串的第i个前缀和T串的第j个前缀的最长公共子序列. ◦          分情况: ◦      ...

  3. .NET/ASP.NETMVC Model元数据、HtmlHelper、自定义模板、模板的装饰者模式(一)

    .NET/ASP.NETMVC Model元数据.HtmlHelper.自定义模板.模板的装饰者模式(一) 阅读目录: 1.开篇介绍 2.Model与View的使用关系(数据上下文DataContex ...

  4. Django——3 模板路径 模板变量 常用过滤器 静态文件的使用

    Django 模板路径 模板变量 过滤器 静态文件的加载 模板的路径,有两种方法来使用 设置一个总的templates在大项目外面,然后在sittings的TEMPLATES中声明 在每一个APP中创 ...

  5. C++:类模板与模板类

    6.3 类模板和模板类 所谓类模板,实际上是建立一个通用类,其数据成员.成员函数的返回值类型和形参类型不具体指定,用一个虚拟的类型来代表.使用类模板定义对象时,系统会实参的类型来取代类模板中虚拟类型从 ...

  6. C++:函数模板与模板函数

    6.1 模板的概念 C++允许用同一个函数定义函数,这些函数的参数个数和参数类型不同.例如求最大值的max函数, int max(int x,int y) {       return (x>y ...

  7. C++ template学习一(函数模板和模板函数)

    函数模板和模板函数(1)函数模板函数模板可以用来创建一个通用的函数,以支持多种不同的形参,避免重载函数的函数体重复设计.它的最大特点是把函数使用的数据类型作为参数.函数模板的声明形式为:templat ...

  8. vs 2013下自定义ASP.net MVC 5/Web API 2 模板(T4 视图模板/控制器模板)

    vs 2013下自定义ASP.net MVC 5/Web API 2  模板(T4 视图模板/控制器模板): Customizing ASP.NET MVC 5/Web API 2 Scaffoldi ...

  9. C++ - 模板类模板成员函数(member function template)隐式处理(implicit)变化

    模板类模板成员函数(member function template)隐式处理(implicit)变化 本文地址: http://blog.csdn.net/caroline_wendy/articl ...

随机推荐

  1. Java学生管理系统(连接数据库查询)超详细

    这几天逼着交Java,借鉴各位师傅的做出来这么个简陋的东西,各位大师傅不要笑我.(学都没有学过Java的我,QAQ~) 下面针对的都是SQL Server系列的连接,如果你使用MySQL那么不必看关于 ...

  2. Cannot convert value '0000-00-00 00:00:00' from column 1 to TIMESTAMP解决办法

    在Mysql数据库中使用DATETIME类型来存储时间,使用JDBC中读取这个字段的时候,应该使用 ResultSet.getTimestamp(),这样会得到一个java.sql.Timestamp ...

  3. Win2D 官方文章系列翻译 - 像素格式

    本文为个人博客备份文章,原文地址: http://validvoid.net/win2d-pixel-formats/ DirectXPixelFormat 枚举 包含了 Direct3D 和 DXG ...

  4. 斗鱼扩展--localStorage备份与导出(九)

    之前我们都把数据 放在了 localStorage 里,但扩展一旦卸载,数据就会被清空, 在Console里备份,一次只能输出一条,小白操作起来很不方便,所以能不能 导入,导出文件来进行备份还原呢? ...

  5. JavaScript写入文件到本地

    工作中有时需要通过 JavaScript 保存文件到本地,我们都知道 JavaScript 基于安全的考虑,是不允许直接操作本地文件的.IE 可以通过 VB 插件的方式进行,而 Chrome 和 fi ...

  6. ZIP文件压缩和解压

    最近要做一个文件交互,上传和下载, 都是zip压缩文件,所以研究了下,写了如下的示例 注意引用  ICSharpCode.SharpZipLib.dll 文件 该dll文件可以到官方网站去下载, 我这 ...

  7. rpm打包工具

    http://fedoraproject.org/wiki/How_to_create_an_RPM_package # rpm --showrc|grep _topdir -14: _builddi ...

  8. 洛谷 P1080 国王游戏

    题目描述 恰逢 H 国国庆,国王邀请 n 位大臣来玩一个有奖游戏.首先,他让每个大臣在左.右手上面分别写下一个整数,国王自己也在左.右手上各写一个整数.然后,让这 n 位大臣排成一排,国王站在队伍的最 ...

  9. May 04th 2017 Week 18th Thursday

    No matter how far you may fly, never forget where you come from. 无论你能飞多远,都别忘了你来自何方. I never forget w ...

  10. 使用selenium启动火狐浏览器,解决Unable to create new remote session问题

    今天用火狐浏览器来做自动化,才启动就报错,提示不能创建新的session,不能启动浏览器 问题原因: 火狐driver与火狐浏览器与selenium版本的不兼容 我使用的火狐driver是0.21.0 ...