PAT 甲级 1007 Maximum Subsequence Sum

题目描述

在这里插入图片描述
从题目名称就可以看出,求和最大的子序列,第一反应是动态规划题

输入描述

在这里插入图片描述

输入样例

10
-10 1 2 3 4 -5 -23 3 7 -21

输出描述

For each test case, output in one line the largest sum, together with the first and the last numbers of the maximum subsequence. The numbers must be separated by one space, but there must be no extra space at the end of a line. In case that the maximum subsequence is not unique, output the one with the smallest indices i and j (as shown by the sample case). If all the K numbers are negative, then its maximum sum is defined to be 0, and you are supposed to output the first and the last numbers of the whole sequence.
注意到K的范围最大到10000,如果采用暴力法遍历每个子序列,一定会超时(O( n2 )(这里有一个经验,在PAT考试里面如果你算出来时间复杂度达到了108(上亿)的数量级 ,一般来说都会超时或内存超限)
如果采用dp(动态规划),设原始数组为a,初始化一个长度为K+1、初始值为0的数组dp,则有:
dp[ i ] = max(dp[ i - 1 ] + a[ i ],a[ i ])
(可以理解为,要么将当前元素拼接在之前的序列后,要么将当前元素作为新序列的开始)
而最后dp数组中的最大值就是要求的最大和。
但是这道题目稍复杂,还要得出这个最长子序列的首部元素和尾部尾部元素。因此,再增加一个数组dpIndex,存储第i个元素所在序列的头部元素的索引。即:

/*实际上就是dp[i-1]>=0*/
if(dp[i-1]+a[i]>=a[i]){
	dp[i] = dp[i-1]+v[i];
	/*如果这个元素选择追加在上一个元素后,则他的dpIndex继承自上一个元素*/
	dpIndex[i] = dpIndex[i-1];
}else{
	dp[i] = a[i];
	dpIndex[i] = i;
}

至于为什么dp[i-1]可以等于0,是因为题目中有这么一句:“In case that the maximum subsequence is not unique, output the one with the smallest indices i and j (as shown by the sample case). ”
考虑一个输入例子:
5
0 0 1 2 3
应该输出
6 0 3
而不是
6 1 3
因为序列“0 0 1 2 3”和序列“1 2 3”的和相同,但是前一个序列的起始元素的索引是0,结束索引是4,后一个序列的起始索引是2,结束索引是4,因此选择前一个序列。
同理,输入为:
5
0 1 2 3 0
输出为
6 0 0

这里有一个特别容易忽视的坑,就是“For each test case, output in one line the largest sum, together with the first and the last numbers of the maximum subsequence. ”,先输出最大和,再输出序列的第一个元素(而不是第一个元素的索引),以及序列的第二个元素。这一点这个样例给的就很有误导性,题目输出样例中,元素的索引和值是相同的。
有很多人的算法会跪在另一个坑上:如果给定的元素中只有负数和0构成,如输入:
5
-1 -2 0 0 -3
则输出
0 0 0
另外,如果给定的数组全是负数,则输出0,和数组中第一个元素和最后一个元素;
完整程序如下:

#include 
#include 
using namespace std;
int main() {
	/*maxVal为最大序列的和,这里的maxVal必须初始化为-1,而不是0,
	否则对于只有0和负数的测试点将无法通过
	maxB为最大序列的起始索引,maxE为最大序列的结束索引,初始化为1
	*/
	int K, maxVal = -1, maxB = 1, maxE = 1;
	scanf("%d", &K);
	/*oriv就是存储原数据的数组*/
	vector<int> dp(K + 1, 0), dpI(K + 1, 1), oriv(K + 1);
	/*isNegative变量用来判断数组元素是否都是负数*/
	bool isNegative = true;
	for (int i = 1; i <= K; i++) {
		scanf("%d", &dp[i]);
		oriv[i] = dp[i];
		if (dp[i] >= 0) isNegative = false;
		if (dp[i - 1] >=0) {
			dp[i] = dp[i - 1] + dp[i];
			dpI[i] = dpI[i - 1];
		}
		else
			dpI[i] = i;
		if (dp[i] > maxVal) {
			maxVal = dp[i];
			maxB = dpI[i];
			maxE = i;
		}
	}
	if (isNegative)
		printf("0 %d %d", oriv[1], oriv[K]);
	else
		printf("%d %d %d", maxVal, oriv[maxB], oriv[maxE]);
	return 0;
}

另外,@柳婼的博客中提出了一种更为有效的方法,1007. Maximum Subsequence Sum (25)-PAT甲级真题(动态规划dp)

你可能感兴趣的:(PAT甲级)