最长不下降子序列

最长不下降子序列(LIS)

在一个数字序列中,找到一个最长的子序列(可以不连续),使得这个子序列是不下降(非递减)的。

题目

例如,现有序列A={1,2,3,-1,-2,7,9}(从下标1开始),它的最长不下降序列是{1,2,3,7,9},长度为5。另外,还有一些子序列是不下降子序列。

分析

令dp[i]表示以A[i]结尾的最长不下降子序列长度。这样对A[i]来说就会有两种可能
1)如果存在A[i]之前的元素A[j](j < i),使得A[j]<=A[i]且dp[j]+1>dpi,那么就把A[i]跟在以A[j]结尾的LIS后面,形成一条更长的不下降子序列(令dp[i]=dp[j]+1)。
2)如果A[i]之前的元素都比A[i]大,那么A[i]就只好自己形成一条LIS,但是长度为1,即这个子序列里面只有一个A[i]。
最后以A[i]结尾的LIS长度就是1)2)中能形成的最大长度。

现有一个序列{1,5,-1,3},其中的元素分别记为A[1]、A[2]、A[3]、A[4]。假设已经知道以A[1]、A[2]、A[3]为结尾的最长不下降子序列分别为{1}、{1,5}、{-1},长度分别为1、2、1。那么如何知道以A[4]结尾的最长不下降子序列及其长度呢?由于必须以A[4]结尾,因此考虑分别把A[4]加到前面以A[1]、A[2]、A[3]结尾的最长不下降子序列后面,看看能不能使以某个Aj为结尾的最长不下降子序列变得更长。

A[4]:喂,A[1]。我可以站在你后面成为更长的LIS
A[1]:我看看,你比我高,当然可以,这样我们组合的新的LIS{1,3}长度就是2了。
A[4]:喂,A[2]。我可以站在你后面成为更长的LIS吗
A[2]:你那么矮,那还是算了,我这里本来长度就有2了,你就算来了也不增加LIS长度。
A[4]:喂,A[3]。我可以站在你后面成为更长的LIS吗
A[3]:你好高啊,当然可以了,站在我后面LIS{-1,3}长度就为2了
这样比较后,A[4]只有加入A[1]或A[3]后面才会形成新的LIS,长度为2.

由此写出状态转移方程:
dp[i] = max{1,dp[j]+1}(j=1,2,…,i-1&&A[j]<A[i])

上面的状态转移方程中隐含边界:dp[i]=1。显然dp[i]只与小于i的j有关,因此只要让i从小到大遍历即可求出整个dp数组。由于dp[i]表示的是以A[i]结尾的LIS长度,因此从整个dp数组中找到最大的那个才是要寻求的整个序列的LIS长度。

#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 100;
int A[N],dp[N];
int main()
{
	int n;
	scanf("%d", &n);
	for(int i = 1; i <= n; i++)
	{
		scanf("%d", &A[i]);
	}
	int ans = -1; //记录最大的dp[i]
	for(int i = 1; i <= n; i++) //按顺序计算出dp[i]的值
	{
		dp[i] = 1; //边界初始条件(即先假设每个元素自成一个子序列)
		for(int j = 1; j < i; j++)
		{
			if(A[i] >= A[j] && (dp[j] + 1 > dp[i]))
			{
				dp[i] = dp[j] + 1; //状态转移方程,用以更新dp[i]
			}
		}
		ans = max(ans ,dp[i]);
	}
	printf("%d", ans);	
	return 0;
}
8
1 2 3 -9 3 9 0 11
6
  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阳光开朗男孩

你的鼓励是我最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值