动态规划2)最大连续子序列和和最长不下降子序列

本文详细解析了使用动态规划解决两个经典问题的方法:最大连续子序列和与最长不下降子序列。通过实例与代码,深入浅出地介绍了状态转移方程的设定与应用,为读者提供了理解动态规划核心思想的清晰路径。

1.最大连续子序列和问题就是:给定一个数字序列a1,a2,a3,a4……an,求i,j(1<=i<=j<=n),使ai+……+aj的和最大,找出最大和。

比如一个序列: -1 10 -2 12 -3 -1
他的最大连续子序列和就是10+(-2)+12=20
用动态规划来处理:假设dp[i]表示以ai作为结尾的连续序列的最大和。比如:
dp[0]=-1;
dp[1]=10;
dp[2]=10+(-2)=8;
dp[3]=10+(-2)+12=20;
dp[4]=10+(-2)+12+(-3)=17;
dp[5]=10+(-2)+12+(-3)+(-1)=16;
dp[i]有两种情况:
1.dp[i]只有a[i]一个元素,这种情况dp[i]就是a[i];
2.dp[i]有多个元素。最大和就是dp[i-1]+a[i]
状态转移方程:dp[i]=max(dp[i-1]+a[i],a[i])
边界就是dp[0]=0,所以写代码的时候就从0开始依次用状态转移方程推出后面的dp[]。

2.最长不下降子序列(LIS):在一个数字序列中找到一个最长的子序列(可以不连续!),让这个序列是递增的。
这个问题要比最大连续子序列复杂一些。我们还是用dp[i]来表示以a[i]结尾的最长不下降子序列。我们用一道例题来具体说明。
在这里插入图片描述
在这里插入图片描述
这两种情况就是最大和的情况:
在这里插入图片描述在这里插入图片描述
用图来分析做法:
在这里插入图片描述
我们可以自己先找出来每一个dp[],做一个分析:
在这里插入图片描述
我们简单分析一下前几个:
在这里插入图片描述
在这里插入图片描述
代码实现:

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int main()
{
	int n;
	scanf("%d",&n);
	int a[n+1];
	int dp[n+1];
	for(int i=0;i<n;i++)
	{
		scanf("%d",&a[i]);
	}
	int ans=-1;//用来找dp[]中的最大值,题目要求输出最大值
	for(int i=0;i<n;i++)
	{
		dp[i]=1;//每一个起始的长度时候都是1
		for(int j=0;j<i;j++)//在i前面找
		{
			if(a[i]>=a[j]&&dp[j]+1>dp[i])
			{
				dp[i]=dp[j]+1;
			}
		}
		ans=max(dp[i],ans);//找到最大dp[]值
	}
	printf("%d",ans);
} 
<think>首先,用户的问题是:“我想了解最大连续子序列问题的非递归思路”,意思是“I want to understand the non-recursive approach to the maximum continuous subsequence problem.” 从系统级指令中: - 我必须使用$...$ 格式行内数学表达式。 - 独立公式使用$$...$$ 并单独成段。 - 使用中文回答。 - 生成相关问题。 - 在引用的段落末尾添加引用标识。 - 参考给定的引用内容。 给定的引用: - 引用[1]:讨论了序列字符的子序列问题,时间复杂度是$2^n$,类似于回溯构造子集。提到了dp数组定义:以第i个元素结尾的子序列最大连续子序列。 - 引用[2]:讨论了动态规划子序列问题,包括最大子序最大连续子序列)。 - 引用[3]:提供了最大连续子序列的思路代码,使用非递归方法(Kadane算法),核心是迭代数组,维护当前子序列最大。 - 引用[4]:提到了最大连续子序列问题其他问题,但用户问题聚焦在非递归思路上。 - 引用[5]:是关于最长公共子序列的代码,与最大连续子序列无关。 用户上一次的问题是关于最大连续子序列问题的非递归思路,所以我需要基于引用[3]来回答,因为它直接提供了非递归的代码。 关键点从引用[3]: - 思路:整个数组的最大连续子序列必定是某个数组元素所在的最大连续子序列。 - 代码解析: - 遍历数组,维护两个变量:thissum(当前元素所在的最大连续子序列),maxsum(全局最大)。 - 如果thissum <= 0,则重新开始(thissum = test[index]),因为负值只会减少。 - 否则,累加当前元素(thissum += test[index])。 - 更新maxsum。 这就是Kadane算法,时间复杂度O(n),空间复杂度O(1)。 我的回答结构: 1. **介绍问题**:简要解释最大连续子序列问题。 2. **描述非递归思路**:基于引用[3]的Kadane算法。 3. **给出伪代码或描述**:用中文描述算法步骤。 4. **时间复杂度分析**:解释为什么高效。 5. **生成相关问题**:在最后添加相关问题。 使用中文回答: - 确保语言清晰、专业。 - 添加引用标识:[^3] 因为主要参考引用[3]。 生成相关问题: - 根据系统指令,在回答结束后添加相关问题。 - 相关问题应与主题相关,如优化、变体、应用等。 例如: 1. Kadane算法如何处理所有负数的情况? 2. 还有其他非递归方法吗? 3. 最大连续子序列动态规划版本? 最终回答框架: - 开头:直接回答问题。 - 主体:逐步解释思路。 - 结尾:添加引用相关问题。 </think>### 最大连续子序列问题的非递归思路 最大连续子序列问题(Maximum Subarray Problem)的目标是找到一个给定数组中元素连续的序列,使其所有元素的最大。非递归思路的核心是避免递归调用,改用迭代方式高效求解。最常用的是 **Kadane算法**,这是一种基于贪心思想的动态规划方法,时间复杂度为$O(n)$,空间复杂度为$O(1)$,特别适用于大规模数据。 #### 思路详解 基于引用[3]的思路,非递归方法的原理是:整个数组的最大连续子序列必定是某个元素结尾的最大连续子序列之一。算法遍历数组一次,维护两个关键变量: - **当前连续(thissum)**:表示以当前元素结尾的连续子序列最大。 - **全局最大(maxsum)**:表示遍历过程中遇到的最大连续子序列。 具体步骤: 1. **初始化**:设置全局最大$maxsum$为负无穷大(或数组第一个元素),当前连续$thissum$为0。 2. **遍历数组**:对于每个元素$arr[i]$: - 如果当前连续$thissum \leq 0$,则说明之前的元素没有贡献(甚至为负),重新开始一个新序列:$thissum = arr[i]$。 - 如果当前连续$thissum > 0$,则累加当前元素:$thissum = thissum + arr[i]$。 - 更新全局最大:$maxsum = \max(maxsum, thissum)$。 3. **结束返回**:遍历完成后,$maxsum$即为最大连续子序列。 这个方法的优势在于: - **高效**:仅需一次遍历,无需额外空间。 - **处理边界**:能正确处理所有元素为负数的情况(此时最大即为数组最大元素)。 - **贪心优化**:通过舍弃负贡献序列前缀,避免必要的计算,体现了贪心策略[^3]。 #### 伪代码描述 以下是非递归实现的伪代码(参考引用[3]的代码解析): ```python def max_continuous_subarray(arr): maxsum = float('-inf') # 初始化为负无穷大 thissum = 0 # 当前连续 for num in arr: if thissum <= 0: # 负或零贡献序列,重新开始 thissum = num else: # 正贡献序列,累加当前元素 thissum += num maxsum = max(maxsum, thissum) # 更新全局最大值 return maxsum ``` #### 时间复杂度分析 - 遍历数组一次,时间复杂度为$O(n)$。 - 空间复杂度为$O(1)$(仅用两个变量),远优于递归($O(n)$递归栈开销)或暴力搜索($O(n^2)$)[^1][^3]。 #### 示例说明 考虑数组$[-2, 1, -3, 4, -1, 2, 1, -5, 4]$: - 遍历过程: - $i=0$: $thissum = -2$, $maxsum = -2$ - $i=1$: $thissum \leq 0$ → $thissum = 1$, $maxsum = 1$ - $i=2$: $thissum > 0$ → $thissum = 1 + (-3) = -2$, $maxsum = 1$ - $i=3$: $thissum \leq 0$ → $thissum = 4$, $maxsum = 4$ - $i=4$: $thissum > 0$ → $thissum = 4 + (-1) = 3$, $maxsum = 4$ - $i=5$: $thissum > 0$ → $thissum = 3 + 2 = 5$, $maxsum = 5$ - 后续步骤更新后,最终$maxsum = 6$(对应子序列$[4, -1, 2, 1]$)。 该方法在LeetCode等平台广泛用于解决类似问题[^2][^3]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

天金小麻花

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值