一、题目
给定 K 个整数的序列{ N1, N2, …, NK },其任意连续子序列可表示为{ Ni, Ni+1, …, Nj },其中 1 <= i <= j <= K。最大连续子序列是所有连续子序列中元素和最大的一个,例如给定序列{ -2, 11, -4, 13, -5, -2 },其最大连续子序列为{ 11, -4, 13 },最大和为 20。现在增加一个要求,即还需要输出该子序列的第一个和最后一个元素。
输入描述:
测试输入包含若干测试用例,每个测试用例占 2 行,第 1 行给出正整数K(K < 10000),第 2 行给出 K 个整数,中间用空格分隔。当 K 为 0 时,输入结束,该用例不被处理。
输出描述:
对每个测试用例,在 1 行里输出最大和、最大连续子序列的第一个和最后一个元素,中间用空格分隔。如果最大连续子序列不唯一,则输出序号 i 和 j 最小的那个(如输入样例的第 2、3 组)。若所有 K 个元素都是负数,则定义其最大和为 0,输出整个序列的首尾元素。
示例 1
输入
6
-2 11 -4 13 -5 -2
10
-10 1 2 3 4 -5 -23 3 7 -21
6
5 -8 3 2 5 0
1
10
3
-1 -5 -2
3
-1 0 -2
0
输出
20 11 13
10 1 4
10 3 5
10 10 10
0 -1 -2
0 0 0
二、题解一
(1)当前面最大子序列和为负数的时候,后面的数加上 sum,其和一定会比后面本身更小,所以把 sum 重置为 0,重新寻找连续最大子序列和
(2)重新寻找的时候,也就是新序列开始的时候,记录下此时的数,可能是最大连续子序列和的开始数
(3)当新的连续最大子序列和大于之前的,说明之前的子序列不是所求,则更新序列的开始数和结尾数
三、代码实现(C++)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| #include <iostream> using namespace std;
int main() { int sum, K, max; while(cin >> K) { if(K == 0) break; sum = 0, max = 0; int nstart, start, nend, first, last; cin >> nstart; first = last = start = max = sum = nend = nstart; for(int i = 1; i < K; i++) { int tmp; cin >> tmp; if(sum < 0) { sum = 0; start = tmp; } sum += tmp; if(sum > max) { max = sum; nstart = start; nend = tmp; } if(i == K - 1) { last = tmp; } } if(max < 0) { cout << 0 << " " << first << " " << last << endl; } else { cout << max << " " << nstart << " " << nend << endl; } } return 0; }
|
四、题解二
DP - 最大连续子序列和问题的变形
记 dp[i] - 以元素 ai(1 <= i <= n)结尾的连续子序列的和,则有两种情况:
- 这个最大和的连续子序列只有一个元素,即以 a[i] 开始,以 a[i] 结尾;
- 这个最大和的连续子序列有多个元素,即从前面某处 a[j] 开始(j < i),一直到 a[i] 结尾。
对第一种情况,最大和即 a[i] 本身;
对第二种情况,最大和即 dp[i-1] + a[i]。
于是得到状态转移方程:
dp[i] = max{ A[i], dp[i-1]+A[i] } (边界条件: dp[1] = a[1])
不过,这里要求判断输入全为负数的情况,并要求输出所求最大连续子序列的起点和终点元素,因此稍作处理即可,本质不变。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
| #include <stdio.h> #define maxn 10010
int n, a[maxn];
struct DP // 以第 i 个元素为终点的连续子序列 { int left; int right; int val; }dp[maxn];
int Judge(int *a, int n) { int i; for(i = 1; i <= n; i++) { if(a[i] >= 0) { return 0; } } return 1; }
int main() { int i; int maxLeft, maxRight, maxVal; while(scanf("%d", &n) != EOF) { if(n == 0) break; for(i = 1; i <= n; i++) { scanf("%d", &a[i]); }
if(Judge(a, n)) { printf("0 %d %d\n", a[1], a[n]); } else { dp[1].left = dp[1].right = 1; dp[1].val = a[1]; for(i = 2; i <= n; i++) { if(a[i] < dp[i-1].val + a[i]) { dp[i].left = dp[i-1].left; dp[i].right = i; dp[i].val = dp[i-1].val + a[i]; } else { dp[i].left = i; dp[i].right = i; dp[i].val = a[i]; } } maxLeft = dp[1].left; maxRight = dp[1].right; maxVal = dp[1].val; for(i = 2; i <= n; i++) { if(dp[i].val > maxVal) { maxVal = dp[i].val; maxLeft = dp[i].left; maxRight = dp[i].right; } } printf("%d %d %d\n", maxVal, a[maxLeft], a[maxRight]); } } return 0; }
|
题目链接
[编程题]最大连续子序列