[PAT_A 1007]Maximum Subsequence Sum

在这里插入图片描述
题目大意:
给一个数字序列 a1 a2 …an,求i,j 使得ai+ai+1…+aj最大,输出最大和以及ai aj。
如果有多种方案,输出ij最小的一组。
如果所有数都小于0,那么认为最大和为0,并输出首尾元素。

思路:
采用动态规划的思想。
令dp[i]表示以A[i]作为末尾的连续序列的最大和。s[i]表示以A[i]作为结尾的最大连续子序列是从那个元素开始。

因为dp[i]要求必须是以A[i]为结尾的连续序列,那么有一下两种情况:
1)最大和的连续序列只有一个元素 即A[i]开始 A[i]结束
2)最大和的连续序列有多个元素,从前面某处的A[p]开始,一直到A[i]结尾。

第一种情况,最大和为A[i]本身,s[i]=i,即只有A[i]一个元素。
第二种情况,最大和为dp[i-1]+A[i]。s[i]=s[i-1],继承上一个元素的起始位置。
于是得到状态转移方程:dp[i]=max{A[i],dp[i-1]+A[i]}

这个式子只与i和i之前的元素有关,且边界为dp[0]=A[0],s[0]=0,由此可以从小到大枚举i,即可以得到整个dp数组。
然后遍历数组,取dp[i]中最大值,即为最大连续子序列的和。再输出首尾下标。

如果输入的全部小于0需要特判。

AC代码:

//PAT_A 1007
#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
const int maxn = 10010;
int main() {
	int n, k = 0;
	(void)scanf("%d", &n);
	vector<int> A(n), dp(n), s(n);
	bool flag = false;//标记是不是全部是负数
	for (int i = 0; i < n; i++) {
		(void)scanf("%d", &A[i]);
		if (A[i] >= 0)flag = true;
	}
	if (flag == false) {//特判 全负
		printf("0 %d %d\n", A[0], A[n - 1]);
		return 0;
	}
	dp[0] = A[0], s[0] = 0;
	for (int i = 1; i < n; i++) {
		if (A[i] > dp[i - 1] + A[i]) {
			dp[i] = A[i];
			s[i] = i;
		}
		else {
			dp[i] = A[i] + dp[i - 1];
			s[i] = s[i - 1];
		}
		if (dp[i] > dp[k])k = i;//保存最大值
	}
	printf("%d %d %d", dp[k], A[s[k]], A[k]);
	return 0;
}
发布了142 篇原创文章 · 获赞 1 · 访问量 4560

猜你喜欢

转载自blog.csdn.net/weixin_44699689/article/details/104668265