浅谈\(DP\):https://www.cnblogs.com/AKMer/p/10437525.html

题目传送门:https://www.luogu.org/problemnew/show/AT2827

\(LIS\)就是\(Longest\) \(Increasing\) \(Subsequence\),最长上升子序列问题。

给你一个序列\(a\),要你求最长上升子序列的长度。

我们可以把序列看做是一个个插数到序列尾部形成的,那么每插入一个新的数字,就是一个新的阶段。我们可以考虑用这个新插入的数字来做上升子序列的结尾,这个上升子序列会有多长。由于每次插入新的数字我们要做的事情都是一样的,所以这个问题就具有子问题重复性。由于我用这个新的数字结尾并不会影响到以以前数字结尾的子序列的长度,所以这个问题满足无后效性。并且,以当前这个数字为结尾的子序列的长度可以由在它之前的比它小的数字结尾的子序列长度得到,所以这个问题满足最优子结构性。于是我们就可以用\(DP\)来做了。

我们可以设立状态\(f_i\)表示以第\(i\)位数字结尾的上升子序列最长的长度是多少。一共有\(n\)个阶段,每次决策选择在\(i\)之前的比\(a_i\)小的数字来更新\(f_i\),状态转移方程就是:\(f_i=max(f_j+1)(0\leqslant j<i,a_j<a_i)\)。\(f\)初始均为\(0\),最后求出整个数组的最大值即为答案。

时间复杂度:\(O(n^2)\)

空间复杂度:\(O(n)\)

代码如下:

#include <cstdio>
#include <algorithm>
using namespace std; const int maxn=1e3+5; int n,ans;
int a[maxn],f[maxn]; int read() {
int x=0,f=1;char ch=getchar();
for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
for(;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0';
return x*f;
} int main() {
n=read();
for(int i=1;i<=n;i++)
a[i]=read();
for(int i=1;i<=n;i++) {
for(int j=0;j<i;j++)
if(a[j]<a[i])
f[i]=max(f[i],f[j]+1);
ans=max(ans,f[i]);
}
printf("%d\n",ans);
return 0;
}

由于\(f_i\)只会由\(f_j+1\)更新得到(\(j\)是满足\(x<i\)且\(a_x<a_i\)的\(f_x\)最大的\(x\)),那么我们完全没有必要枚举没有用的位置,那样对于时间的浪费就太大。所以我们可以对于这个方程进行改进。

设\(f_i\)为以数字\(i\)结尾的最长上升子序列的长度。那么\(f_i=max(f_j+1)(0\leqslant j <i)\),因为我们的阶段还没有进行到\(i\)后面去,所以\(f_j\)表示的必然是\(i\)前面的某一个数字\(j\)结尾的上升子序列长度。这样子我们就可以用树状数组来维护\(f\)数组,每次查询前缀最大值\(+1\)来更新当前的\(f_{a_i}\),然后再在树状数组上改动相应的位置即可。如果序列的值域一开始不在\([1,n]\),那么我们离散化即可。

时间复杂度:\(O(nlogn)\)

空间复杂度:\(O(n)\)

代码如下:

#include <cstdio>
#include <algorithm>
using namespace std;
#define low(i) ((i)&(-(i))) const int maxn=1e5+5; int n,cnt;
int tmp[maxn],a[maxn]; int read() {
int x=0,f=1;char ch=getchar();
for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
for(;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0';
return x*f;
} struct tree_array {
int c[maxn]; void change(int pos,int v) {
for(int i=pos;i<=cnt;i+=low(i))
c[i]=max(c[i],v);
} int query(int pos) {
int res=0;
for(int i=pos;i;i-=low(i))
res=max(res,c[i]);
return res;
}
}T; int main() {
n=read();
for(int i=1;i<=n;i++)
tmp[i]=a[i]=read();
sort(tmp+1,tmp+n+1);
cnt=unique(tmp+1,tmp+n+1)-tmp-1;
for(int i=1;i<=n;i++) {
a[i]=lower_bound(tmp+1,tmp+cnt+1,a[i])-tmp;
int f=T.query(a[i]-1)+1;
T.change(a[i],f);
}
printf("%d\n",T.query(n));
return 0;
}

洛谷【AT2827】LIS的更多相关文章

  1. 【模板】最长上升子序列(LIS)及其优化 & 洛谷 AT2827 LIS

    最长上升子序列 传送门 题意 对于给定的一个n个数的序列,找到它的一个最长的子序列,并且保证这个子序列是由低到高排序的. 例如,1 6 2 5 4 6 8的最长上升子序列为1 2 4 6 8. 基本思 ...

  2. 洛谷 AT2827 LIS

    题目传送门 解题思路: 用f[i]表示长度为i的最长上升子序列的最小的末尾. AC代码: #include<iostream> #include<cstdio> #includ ...

  3. P1091 合唱队形题解(洛谷,动态规划LIS,单调队列)

    先上题目 P1091 合唱队形(点击打开题目) 题目解读: 1.由T1​<...<Ti​和Ti​>Ti+1​>…>TK​可以看出这题涉及最长上升子序列和最长下降子序列 2 ...

  4. 洛谷P1108 低价购买[DP | LIS方案数]

    题目描述 “低价购买”这条建议是在奶牛股票市场取得成功的一半规则.要想被认为是伟大的投资者,你必须遵循以下的问题建议:“低价购买:再低价购买”.每次你购买一支股票,你必须用低于你上次购买它的价格购买它 ...

  5. 【模板】LIS模板 洛谷P1091 [NOIP2004提高组]合唱队形 [2017年4月计划 动态规划11]

    以题写模板. 写了两个:n^2版本与nlogn版本 P1091 合唱队形 题目描述 N位同学站成一排,音乐老师要请其中的(N-K)位同学出列,使得剩下的K位同学排成合唱队形. 合唱队形是指这样的一种队 ...

  6. 洛谷1439 排列LCS问题

    洛谷1439 排列LCS问题 本题地址:http://www.luogu.org/problem/show?pid=1439 题目描述 给出1-n的两个排列P1和P2,求它们的最长公共子序列. 输入输 ...

  7. 洛谷P3928 Sequence2(dp,线段树)

    题目链接: 洛谷 题目大意在描述底下有.此处不赘述. 明显是个类似于LIS的dp. 令 $dp[i][j]$ 表示: $j=1$ 时表示已经处理了 $i$ 个数,上一个选的数来自序列 $A[0]$ 的 ...

  8. DP【洛谷P1704】 寻找最优美做题曲线

    [洛谷P1704] 寻找最优美做题曲线 题目背景 nodgd是一个喜欢写程序的同学,前不久(好像还是有点久了)洛谷OJ横空出世,nodgd同学当然第一时间来到洛谷OJ刷题.于是发生了一系列有趣的事情, ...

  9. 洛谷1439:最长公共子序列(nlogn做法)

    洛谷1439:最长公共子序列(nlogn做法) 题目描述: 给定两个序列求最长公共子序列. 这两个序列一定是\(1\)~\(n\)的全排列. 数据范围: \(1\leq n\leq 10^5\) 思路 ...

随机推荐

  1. TIJ读书笔记01-操作符

      TIJ读书笔记01-操作符 概述 关系操作符和逻辑操作符 位操作符 类型转换 概述 操作符 操作符接受一个或多个参数,并生成一个新值. 换句话说操作符作用于操作数,生成一个新值.有些操作符会改变操 ...

  2. 【leetcode刷题笔记】Surrounded Regions

    Given a 2D board containing 'X' and 'O', capture all regions surrounded by 'X'. A region is captured ...

  3. level-4

    [1.网页乱码的问题是如何产生的.怎么样解决?] 造成html网页乱码原因主要是html源代码内中文字内容与html编码不同造成.主要情况有以下三种: 1.比如网页源代码是gbk的编码,而内容中的中文 ...

  4. java web数据库连接封装-simple

    package cn.cslg.bm.web.util; import java.sql.Connection; import java.sql.DriverManager; import org.a ...

  5. 前段时间说了AssetBundle打包,先设置AssetLabels,再执行打包,但是这样有个弊端就是所有设置了AssetLabels的资源都会打包,这次说说不设置AssetLabels,该如何打包AssetBundle

    BuildPipeline.BuildAssetBundles() 这个函数,有多个重载,一个不用AssetBundleBuild数组,一个需要,如果设置了AssetLabels,那么这时候是不需要的 ...

  6. 理解Android Build系统【转】

    本文转载自:http://www.ibm.com/developerworks/cn/opensource/os-cn-android-build/ Android Build 系统是用来编译 And ...

  7. python之命令行解析工具argparse

    以前写python的时候都会自己在文件开头写一个usgae函数,用来加上各种注释,给用这个脚本的人提供帮助文档. 今天才知道原来python已经有一个自带的命令行解析工具argparse,用了一下,效 ...

  8. Eclipse Task的使用

    参考链接:http://blog.csdn.net/limb99/article/details/8881891; http://hi.baidu.com/jinxv1987/item/64496f6 ...

  9. Eclipse开发快捷键精选

    1.alt+?或alt+/:自动补全代码或者提示代码2.ctrl+o:快速outline视图3.ctrl+shift+r:打开资源列表4.ctrl+shift+f:格式化代码5.ctrl+e:快速转换 ...

  10. RHEL 7 安装 ngnix

    安装ngnix yum install -y make apr* autoconf automake curl curl-devel gcc gcc-c++ gtk+-devel zlib-devel ...