先学习下LIS最长上升子序列

​ 看了大佬的文章OTZ:最长上升子序列 (LIS) 详解+例题模板 (全),其中包含普通O(n)算法*和以LIS长度及末尾元素成立数组的普通O(nlogn)算法,当然还有本文涉及的树状数组维护后的O(nlogn)算法*

再贴一个容易理解的树状数组算法:https://www.cnblogs.com/war1111/p/7682228.html

再看看这道题

原题链接:http://acm.hnucm.edu.cn/JudgeOnline/problem.php?id=1373

题目描述

给定一个序列 a,求去除 a 中一段连续长度为 L 的序列后,a 的最长不下降子序列的长度的最大值。

输入

单组数据。

第一行两个整数 n,L 表示序列的长度为 n,L 如题意所示。

第二行 n 个数表示序列 a

n ≤ 105, 0 ≤ L ≤ n

输出

输出一个整数表示最长不下降子序列长度的最大值

样例输入

6 3

2 1 3 6 4 5

样例输出

3

思路:假设数组 dp[i] 为 以 a[i] 结尾的LIS

​ dn[i] 为 以 a[i] 结尾并截取 l 长度后的最优LIS

状态转移:

​ 1、首先应该想到在 i < L 时 dn[i] = 0;

​ 2、其次考虑 i>=L

​ ① 截取的 l 长度为 a[i-L] ~ a[i-1] 时,只需考虑把 a[i] 加在 a[1] ~ a[i-L-1] 后面,即满足条件的 dp[1] ~ dp[i-L-1] 中的最大值。 dn[i] = max { dp[j]+1 } (1 <= j < i - L , A[j] < A[i] )

​ ② 截取的 l 长度在 ①条件 之前,那么这个时候是不是可以在 dn 数组本身去找,因为前面的dn就是截取后的呀,即满足条件的 dn[1] ~ dn[i-1]中的最大值。 dn[i] = max { dn[j]+1 } (1 <= j < i , A[j] < A[i] )

​ 最后 ans 就是 dni中的最大值 <截取在 i 之前> 以及 dpi中最大<截取在 i 之后>。

dp思路类似于 蓝桥杯——最大的算式

代码实现

#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 1e5;
int a[maxn+5],dp[maxn+5],dn[maxn+5];
int main()
{
int n,k,ans=0;
scanf("%d%d",&n,&k);
for(int i=0; i<n; i++){
scanf("%d",&a[i]);
} //求 dp[i] , O(n^2)
for(int i=0; i<n; i++){
dp[i] = 1;
for(int j=0; j<i; j++){
if(a[i] >= a[j]){
dp[i] = max(dp[i],dp[j]+1);
}
}
}
//求dn[i]
for(int i=0; i<n; i++){
dn[i] = 0;
if(i>=k){
for(int j=0; j<i-k; j++)
if(a[i] >= a[j])
dn[i] = max(dn[i],dp[j]+1);
for(int j=k; j<i; j++){
if(a[i] >= a[j]){
dn[i] = max(dn[i],dn[j]+1);
}
}
ans = max(ans, dn[i]);
}
} printf("%d\n",ans);
return 0;
}

点击并拖拽以移动

​ 相信思路应该很清晰吧,对于之前做 dp 都是读完题万年懵,一看题解知天下,这道题当时能把状态转移搞清楚真的很有成就感,BUT 超时超时超时!!!

其实思路不变,按照LIS树状数组思路去做,对应的 dn[i] 也开一个对应的树状数组存储,用到了离散化,当输入值比较大则会超出树状数组范围,但是输入数量一定,就按照输入值的大小顺序及关系(大于,小于,等于)重新赋值覆盖输入值。

在hnucm平台上这道题提交了20多次测试,感谢不杀之恩orz:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+10;
int t[2][maxn],dp[maxn],dn[maxn],n,L,ans=0;
struct Node {int val,num;} a[maxn];
bool cmp(Node a,Node b){
return a.val<b.val;
}
bool cmp1(Node a,Node b){
return a.num<b.num;
} // T[x] 表示目前以数值 x 结尾的最大长度;t[0][x]对应dp[i]的树状数组,t[1][x]对应dn[i]的树状数组
int query(int p,int x){
int cnt = 0;
while(x) cnt=max(cnt,t[p][x]), x-=x&-x;
return cnt;
}
int update(int p,int x,int m){
while(x<=n) t[p][x]=max(t[p][x],m), x+=x&-x;
}
int main()
{
scanf("%d%d",&n,&L);
for(int i=1;i<=n;i++){
scanf("%d",&a[i].val);
a[i].num = i;
}
sort(a+1,a+1+n,cmp);
a[1].val = 1;
for(int i=2,k=1;i<=n;i++){
if(a[i].val==a[i-1].val) a[i].val = k;
else a[i].val = ++k;
}
sort(a+1,a+1+n,cmp1);
for(int i=1; i<=n; i++){
dp[i] = query(0,a[i].val) +1; //每次遍历的时候用 dp[i] 去查询更新当前的 T[a[i]]
update(0,a[i].val,dp[i]);
if(i>L){
dn[i] = query(1,a[i].val) +1;
update(1,a[i].val,dn[i]); //在已截取的基础上寻找LIS
update(1,a[i-L].val,dp[i-L]); //截取当前元素的前L个
ans = max(ans,dn[i]);
}
}
for(int i=1;i<=n-L;i++){ //如果截取的L在最优LIS后面,取出来没有截取的就行
ans=max(ans,dp[i]);
}
printf("%d\n",ans);
return 0;
}

算法进阶 (LIS变形) 固定长度截取求最长不下降子序列【动态规划】【树状数组】的更多相关文章

  1. P1020 导弹拦截(nlogn求最长不下降子序列)

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

  2. JDOJ 1946 求最长不下降子序列个数

    Description 设有一个整数的序列:b1,b2,…,bn,对于下标i1<i2<…<im,若有bi1≤bi2≤…≤bim 则称存在一个长度为m的不下降序列. 现在有n个数,请你 ...

  3. HDU 6357.Hills And Valleys-字符串非严格递增子序列(LIS最长非下降子序列)+动态规划(区间翻转l,r找最长非递减子序列),好题哇 (2018 Multi-University Training Contest 5 1008)

    6357. Hills And Valleys 自己感觉这是个好题,应该是经典题目,所以半路选手补了这道字符串的动态规划题目. 题意就是给你一个串,翻转任意区间一次,求最长的非下降子序列. 一看题面写 ...

  4. BZOJ2124: 等差子序列(树状数组&hash -> bitset 求是否存在长度为3的等差数列)

    2124: 等差子序列 Time Limit: 3 Sec  Memory Limit: 259 MBSubmit: 2354  Solved: 826[Submit][Status][Discuss ...

  5. 求最长不下降子序列(nlogn)

    最长递增子序列问题:在一列数中寻找一些数,这些数满足:任意两个数a[i]和a[j],若i<j,必有a[i]<a[j],这样最长的子序列称为最长递增子序列. 设dp[i]表示以i为结尾的最长 ...

  6. Monkey and Banana(dp,求最长的下降子序列)

    A group of researchers are designing an experiment to test the IQ of a monkey. They will hang a bana ...

  7. [noip科普]关于LIS和一类可以用树状数组优化的DP

    预备知识 DP(Dynamic Programming):一种以无后效性的状态转移为基础的算法,我们可以将其不严谨地先理解为递推.例如斐波那契数列的递推求法可以不严谨地认为是DP.当然DP的状态也可以 ...

  8. 最长不下降子序列的O(n^2)算法和O(nlogn)算法

    一.简单的O(n^2)的算法 很容易想到用动态规划做.设lis[]用于保存第1~i元素元素中最长不下降序列的长度,则lis[i]=max(lis[j])+1,且num[i]>num[j],i&g ...

  9. 【转】关于LIS和一类可以用树状数组优化的DP 预备知识

    原文链接 http://www.cnblogs.com/liu-runda/p/6193690.html 预备知识 DP(Dynamic Programming):一种以无后效性的状态转移为基础的算法 ...

随机推荐

  1. Java进阶专题(十三) 从电商系统角度研究多线程(上)

    前言 ​ 本章节主要分享下,多线程并发在电商系统下的应用.主要从以下几个方面深入:线程相关的基础理论和工具.多线程程序下的性能调优和电商场景下多线程的使用. 多线程J·U·C 线程池 概念 回顾线程创 ...

  2. vue require.context自动化导入

    语法: require.context(directory, useSubdirectories = false, regExp = /^.//); directory {String} -读取文件的 ...

  3. 01_Python基础知识梳理

    1.计算机知识基础 1.计算机组成 计算机底层: 点子电路,计算机只能识别两个数 0 1         硬件: 处理器(CPU), 运行内存(RAM), 主板(总线设备), 外部存储设备(硬盘U盘等 ...

  4. Linux:基础命令三

    一.软链接 相当于windows中的快捷方式,为了方便用户在使用时更快找到 ln -s /application/appche2.2.0/  /application/appche       注意: ...

  5. 云计算openstack共享组件——Memcache 缓存系统(4)

    一.缓存系统 一.静态web页面: 1.在静态Web程序中,客户端使用Web浏览器(IE.FireFox等)经过网络(Network)连接到服务器上,使用HTTP协议发起一个请求(Request),告 ...

  6. python的学习准备工作

    python是开放的的语言,可以从官方网站下载www.python.org 下载下来后直接运行安装就行了 ctrl+n新建一个文件 ctrl+s保存,以.py为扩展名 点run module 或者F5 ...

  7. Redis5设计与源码分析读后感(四)压缩列表

    一.引言 上一节我们总结了跳跃表的知识,我们知道了有序数组可以用跳跃表实现,也可以用压缩列表来实现,这一篇文章我们来总结一下压缩列表相关的知识. 二.压缩列表简介 定义:压缩列表 ziplist 本质 ...

  8. linux学习(二)认识Linux

    一.Linux系统的组成 linux内核(linus 团队管理) shell:用户与内核交互的接口 文件系统:ext3.ext4等.windows 有 fat32 .ntfs 第三方应用软件 二.Li ...

  9. kubeadm单集群部署k8s1.15.1&flannel网络

    说明 本次实验在Windows下的VMware进行 系统配置及初始化配置在所有的主机执行 容器镜像全部替换为国内可拉取的 pod网络采用flannel 实验环境 主机名 IP地址 角色 OS CPU/ ...

  10. java并发编程--Synchronized的理解

    synchronized实现锁的基础:Java中每一个对象都可以作为锁,具体表现为3种形式. (1)普通同步方法,锁是当前实例对象 (2)静态同步方法,锁是当前类的Class对象 (3)同步方法块,锁 ...