动态规划法-最大连续子序列和问题

一、问题描述

输入

  • 第一行包含一个正整数 N,表示序列中数字的数量。
  • 第二行包含 N 个整数,表示序列中的数。

输出

  • 对于每一组输入数据,输出一个数,表示最大序列和。
  • 如果序列中所有数都是负数,最大序列和为 0。

规定

  • 一个序列的最大连续子序列和至少是 0。
  • 如果序列和小于 0,则结果为 0。
  • 输入可能包括多组数据,对于每一组输入数据,仅输出一个数,表示最大序列和。

变量条件

  • N 为正整数,N ≤ 1,000,000。
  • 结果序列和在范围 (-2^63, 2^63 - 1) 以内。

示例

输入

5
1 5 -3 2 4
6
1 -2 3 4 -10 6
4
-3 -1 -2 -5

输出

9
7
-1

说明

  • 给定一个整数序列 S,其中有 N 个数。
  • 定义其中一个非空连续子序列 T,其中所有数的和为 T 的“序列和”。
  • 对于 S 的所有非空连续子序列 T,求最大的序列和。

二、问题求解

问题概述

我们有一个数字序列,例如 1, -2, 3, 4, -10, 6。我们的目标是找出这个序列中连续数字相加得到的最大和。

动态规划数组 dp

我们使用一个数组 dp 来存储每个位置 i 的最大连续子序列和,即 dp[i] 表示以第 i 个数结尾的最大连续子序列和。

处理方法

对于序列中每个位置的数字,我们有两种处理方式:

  1. 如果 dp[i] 大于 0,这意味着加上它后面的数字会使子序列和增大,因此我们保留这个数字。
  2. 如果 dp[i] 小于 0,这意味着加上它后面的数字会使子序列和减小,所以我们舍弃前面的数字,并从第 i+1 个数字开始重新计算连续子序列和。

状态转移方程

对于每个位置 i,我们更新 dp[i] 的值:

  • 如果 dp[i-1] + a[i] 大于 a[i],则 dp[i] 更新为 dp[i-1] + a[i],表示保留前面的数字。
  • 否则,dp[i] 更新为 a[i],表示从当前数字重新开始计算。

算法步骤

  1. 从序列的第二个数字开始遍历。
  2. 对于每个数字,我们计算 dp[i]
    • 如果 dp[i-1] + a[i] 大于 a[i],则说明前面的数字有助于增大子序列和,更新 dp[i]dp[i-1] + a[i]
    • 否则,dp[i] 更新为 a[i],表示从当前数字重新开始计算子序列和。
  3. 遍历结束后,dp 数组中的最大值就是整个序列的最大连续子序列和。

寻找最大值

在遍历过程中,我们可以使用一个整型变量来不断更新并记录 dp 数组中的最大值。

三、代码

#include <iostream>
using namespace std;
#define MAXN 20

int dp[MAXN]; // 定义一个全局数组dp,用于存储到每个位置为止的最大连续子序列和

// 函数用于计算最大连续子序列和
void max_sub_sum(int a[], int len) {
	dp[0] = 0; // 初始化dp数组的第一个元素为0
	for (int i = 1; i <= len; i++) {
		dp[i] = max(dp[i - 1] + a[i - 1], a[i - 1]); // 状态转移方程
	}
}

// 函数用于展示最大连续子序列和及其对应的子序列
void show_result(int a[], int len) {
	int maxi = 0; // 用于记录最大连续子序列和对应的索引
	for (int i = 1; i <= len; i++) {
		if (dp[i] > dp[maxi]) {
			maxi = i; // 更新最大连续子序列和对应的索引
		}
	}
	int str; // 用于记录最大连续子序列的起始位置
	for (str = maxi; str >= 0; str--) {
		if (dp[str] < 0) {
			break; // 从后向前找到第一个使得子序列和小于0的位置
		}
	}
	cout << "求解结果" << endl;
	cout << "\t最大连续子序列和:" << dp[maxi] << endl; // 输出最大连续子序列和
	cout << "\t最大连续子序列:"; // 输出最大连续子序列
	for (int i = str + 1; i <= maxi; i++) {
		cout << a[i - 1] << " "; // 打印子序列
	}
	cout << endl;
}

int main() {
	int a[] = { -2, 11, -4, 13, -5, -2 }; // 定义一个数组a,存储序列
	int len = sizeof(a) / sizeof(a[0]); // 计算数组a的长度
	max_sub_sum(a, len); // 调用函数计算最大连续子序列和
	show_result(a, len); // 调用函数展示结果
	return 0;
}

在这里插入图片描述

时间复杂度

由于算法仅包含一重循环,时间复杂度为:T(n) = O(n)

  • 8
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值