这次我们来讲解一个叫做“最长非下降子序列”的问题及他的O(n^2)解法。

首先我们来描述一下什么是“最长非下降子序列”。

给你一个长度为n的数组a,在数组a中顺序找到最多的元素(这些元素的顺序不能乱,但是可以不连续),使得这些找出的元素最多,同时要保证找出的元素的数列中前面的元素要小于等于后面的元素,则这些元素组成的一个新的数组就是这个数组的最长非下降子序列。

符合这样的一个要求的问题就是“最长非下降子序列”问题。其中最重要的就是前一个元素的值要小于等于后一个元素的值。

例如,给定一个数组a,其元素排列如下:

1 3 2 4 5

1 3 4 5

1 2 4 5

都是其最长非下降子序列。

如果将条件“小于等于”稍加改变,就会衍生出另外的一些问题,如:

  • 小于:最长上升子序列
  • 大于:最长下降子序列
  • 大于等于:最长非下降子序列

不过我们可以很明显的看到,这些解决的都是同样的问题!所以我们在这个题目里面讲只重点讲解“最长非下降子序列”问题。

求最长非下降子序列有O(N*logN)的解法,不过我们在这篇随笔中先介绍O(n^2)的解法,以后有机会再介绍前面这种解法。

O(n^2)解法 —— 动态规划解法

我们可以发现,对于一个长度为n的数组a,我们同时开一个长度为n的数组f,其中f[i]表示以第i个元素结尾的最长上升子序列的长度,则状态转移方程可以表示为:

f[i]=max{f[j]+1},其中0<=j<=i-1,并且a[j]<=a[i]

求解数组a的最长上升子序列的长度的代码如下:

#include <iostream>
using namespace std;
int a[10010], f[10010], n, s;
int main()
{
cin >> n;
for (int i = 0; i < n; i ++)
cin >> a[i];
s = 0;
for (int i = 0; i < n; i ++)
{
f[i] = 1;
for (int j = 0; j < i; j ++)
{
if (a[j] <= a[i])
{
f[i] = max(f[i], f[j] + 1);
}
}
if (f[i] > f[s])
{
s = i;
}
}
cout << "max=" << f[s] << endl;
return 0;
}

注意:代码中的变量s用于保存最长连续子序列的最后一个数的坐标,则其对应的f[s]即为最长连续子序列的长度。

输出一个最长非下降子序列

有的时候题目不仅要求最长非降子序列的长度,还要求这个最长非降子序列(即完整地输出这个最长非下降子序列),这个时候应该怎么办呢?

我们可以再开一个长度为n的数组pre,pre[i]表示第i个元素所在的最长非下降子序列中他的前一个元素的坐标,那么,如果我们可以编写如下的一个递归函数来顺序输出一个最大非下降子序列:

{
if (i == -1)
return;
output(pre[i]);
cout << a[i] << " ";
}

同时,我们在原来的程序当做稍作修改,增加了pre函数的初始化和pre的计算部分,改进的代码如下:

#include <iostream>
using namespace std;
int a[10010], f[10010], pre[10010], n, s;
void output(int i)
{
if (i == -1)
return;
output(pre[i]);
cout << a[i] << " ";
}
int main()
{
cin >> n;
for (int i = 0; i < n; i ++)
cin >> a[i];
s = 0;
for (int i = 0; i < n; i ++)
pre[i] = -1;
for (int i = 0; i < n; i ++)
{
f[i] = 1;
for (int j = 0; j < i; j ++)
{
if (a[j] <= a[i])
{
if (f[j] + 1 > f[i])
{
f[i] = f[j] + 1;
pre[i] = j;
}
}
}
if (f[i] > f[s])
{
s = i;
}
}
cout << "max=" << f[s] << endl;
output(s);
return 0;
}

该函数可以输出数组a的一个最长非下降子序列。

输出字典序最小的最长非下降子序列

按照我们之前的例子:

1 3 2 4 5

他有两个最长非下降子序列1 3 4 51 2 4 5,如果我们运行了上面的程序,当我们输入:

5
1 3 2 4 5

的时候,出来的结果是

max=4
1 3 4 5

如果我们要求输出的最长非下降子序列是字典序最小的那个,则程序就出错了,原因在于我们没有对“字典序最小”这个条件进行处理。

那么怎么对“字典序最小”这个条件进行处理呢?我们就要稍微修改一下动态规划中求pre的状态转移方程了!

原来的状态转移方程式这样的:

            if (a[j] <= a[i])
{
if (f[j] + 1 > f[i])
{
f[i] = f[j] + 1;
pre[i] = j;
}
}

要考虑字典序最小,我们在判断的时候不光要判断f[j] + 1 > f[i]的情况,还要判断f[j] + 1 == f[i]的情况,当f[j] + 1 == f[i]时,我们要判断一下a[j]和a[pre[i]]哪个小,如果a[j]要小,则我们必须要果断地将pre[i]的值改为j。

该部分改进的代码如下:

                if (f[j] + 1 > f[i])
{
f[i] = f[j] + 1;
pre[i] = j;
}
if (f[j] + 1 == f[i])
{
if (pre[i] != -1 && a[j] < a[pre[i]])
pre[i] = j;
}

我们假设数组a的最长非下降子序列的长度为m,则通过上面的处理,我们已经保证了最长非下降子序列的前m-1个数是字典序最小的,那么怎么保证最后一个数是字典序最小的呢?

这个时候我们就要稍微处理一下s了(因为s是保存最后一个数的坐标的),我们原来的代码是这样的:

        if (f[i] > f[s])
{
s = i;
}

但是光考虑f[i] > f[s]的情况在这里是不够的,我们还要考虑一下f[i] == f[s]的情况,在这种情况下我们要比较一个a[i]和a[s]的大小,如果a[i]<a[s],则s应该被置为i,该部分改进的代码如下:

        if (f[i] > f[s])
{
s = i;
}
if (f[i] == f[s])
{
if (a[i] < a[s])
s = i;
}

所以,求一个数组a的字典序最小的最长非下降子序列的杨丽程序如下:

#include <iostream>
using namespace std;
int a[10010], f[10010], pre[10010], n, s;
void output(int i)
{
if (i == -1)
return;
output(pre[i]);
cout << a[i] << " ";
}
int main()
{
cin >> n;
for (int i = 0; i < n; i ++)
cin >> a[i];
s = 0;
for (int i = 0; i < n; i ++)
pre[i] = -1;
for (int i = 0; i < n; i ++)
{
f[i] = 1;
for (int j = 0; j < i; j ++)
{
if (a[j] <= a[i])
{
if (f[j] + 1 > f[i])
{
f[i] = f[j] + 1;
pre[i] = j;
}
if (f[j] + 1 == f[i])
{
if (pre[i] != -1 && a[j] < a[pre[i]])
pre[i] = j;
}
}
}
if (f[i] > f[s])
{
s = i;
}
if (f[i] == f[s])
{
if (a[i] < a[s])
s = i;
}
}
cout << "max=" << f[s] << endl;
output(s);
return 0;
}

最长非降子序列的O(n^2)解法的更多相关文章

  1. 最长非降子序列的N*logN解法

    之前讲到过求最长非降子序列的O(N^2)解法. 链接 这次在原来的基础上介绍一下N*logN解法. 该解法主要是维护一个数组minValue,minValue[i]表示最长上身子序列长度为i的数的最小 ...

  2. 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 ...

  3. DP:凑零钱问题/最长非降子序列(C++)

    你给出一定数额的钱 i 元给我,我利用手中的硬币(m元, j元, k元...)兑换等值的钱给你,要求硬币数最少. 举例:给出1-11的钱,手中硬币有1元,3元,5元. 重点是找到状态和状态转移方程. ...

  4. 最长非降/下降子序列问题(DP)(待续...)

    注意:抽象成以下描述即为最长非降/下降子序列问题(一维状态) 问题描述:在一个无序的序列a1,a2,a3,a4…an里,找到一个最长的序列满足:(不要求连续) ai<=aj<=ak…< ...

  5. 求最长非降(递增)子序列LIS的长度,及注意事项

    非降序列(Increasing Sequence)例如: (1) 完全递增型序列:S={1,3,6,7,9} (2) 部分存在等于的序列:S={1,3,3,6,9} S的非降子序列:由原序列S的元素组 ...

  6. HDU 6357.Hills And Valleys-动态规划(区间翻转l,r找最长非递减子序列)

    题意:给一串由n个数字组成的字符串,选择其中一个区间进行翻转,要求翻转后该字符串的最长非降子序列长度最长,输出这个最长非降子序列的长度以及翻转的区间的左右端点 #include<bits/std ...

  7. HDU 1025-Constructing Roads In JGShining's Kingdom(最长不降子序列,线段树优化)

    分析: 最长不降子序列,n很大o(n^2)肯定超,想到了小明序列那个题用线段树维护前面的最大值即可 该题也可用二分搜索来做. 注意问题输出时的坑,路复数后加s #include <map> ...

  8. 题目:[NOIP1999]拦截导弹(最长非递增子序列DP) O(n^2)和O(n*log(n))的两种做法

    题目:[NOIP1999]拦截导弹 问题编号:217 题目描述 某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统.但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发 ...

  9. [LeetCode] Longest Uncommon Subsequence I 最长非共同子序列之一

    Given a group of two strings, you need to find the longest uncommon subsequence of this group of two ...

随机推荐

  1. jsp/html页面中的路径

    Html/Jsp页面中的路径,是供浏览器使用的."/"代表的是服务器根目录,一个服务器会有多个web应用,所以请求资源时需要加应用名才能正确访问. 页面中使用的相对路径,也是由浏览 ...

  2. sublime text3 支持终端打开文件

    sudo ln -s /Applications/Sublime\ Text.app/Contents/SharedSupport/bin/subl /usr/local/bin/subl

  3. Dockerfile 最佳实践

    之前 一篇文章介绍 docker 的镜像基本原理和概念 ,主要介绍在编写 docker 镜像的时候一些需要注意的事项和推荐的做法. 虽然 Dockerfile 简化了镜像构建的过程,并且把这个过程可以 ...

  4. 【面向对象设计原则】之里氏替换原则(LSP)

    里氏代换原则由2008年图灵奖得主.美国第一位计算机科学女博士Barbara Liskov教授和卡内基·梅隆大学Jeannette Wing 教授于1994年提出,所以使用的是这位女博士的性命名的一个 ...

  5. 【Netty】WebSocket

    一.前言 前面学习了codec和ChannelHandler之间的关系,接着学习WebSocket. 二.WebSocket 2.1. WebSocket介绍 WebSocket协议允许客户端和服务器 ...

  6. 使用Criteria 实现两表的左外连接,返回根对象

    (转) 引用 两个实体 Parent(P) 和 Child(C)之间是1:N的关系,现要求符合指定条件的P及所包 含的C 采用hibernate中的Criteria来实现此功能的代码如下: Java代 ...

  7. python学习第三个坑

    ##########################python 第三章 ################################这一章呢,主要是文件的操作,还有涉及到函数的一部分. PS:整 ...

  8. 如何用python绘制各种图形

    1.环境 系统:windows10 python版本:python3.6.1 使用的库:matplotlib,numpy 2.numpy库产生随机数几种方法 import numpy as np nu ...

  9. JDBC复习

    -----------------------------------------JDBC复习----------------------------------------- 1.JDBC (Jav ...

  10. javascript走马灯的效果(文档标题文字滚动)

    做一些网站的时候,文档标题会滚动,这个效果是走马灯的效果. <!DOCTYPE html> <html> <head> <meta charset=" ...