思路: 将原数组递归分解为两个子数组,直到能求解出子解为止,再将答案合并。
难点:产生数组的最大子序列只有以下三种情况:
1.最大子序列为左子数组的最大子序列;
2.最大子序列为右子数组的最大子序列;
3.最大子序列为跨中点的最大子序列;(特殊情况)
依照以上三种情况分而治之即得解。
附ac代码
#include <stdio.h>
int a[100001];
void subcross(int arr[], int *low, int *mid, int *high, int *sum){
int s = 0, maxl = -1000 - 1, maxr = -1000 - 1, l, r;
for(int i = *mid; i >= *low; --i){ //以中点为基准,向左推进求最大左边界子序列
s += arr[i];
if(s >= maxl){
l = i; maxl = s;
}
}
for(int i = *mid + 1, s = 0; i <= *high; ++i){ //以中点为基准,向右推进求最大右边界子序列
s += arr[i];
if(s > maxr){
r = i; maxr = s;
}
}
*sum = maxl + maxr; //合并,传递参数
*low = l;
*high = r;
}
void subarr(int arr[], int *low, int *high, int *sum){
if(*low == *high){ //base case
*sum = arr[*low];
return;
}
int l = *low, r = *high, s1, s2, s;
int m = (l + r) / 2;
int l1 = l, l2 = m + 1, r1 = m, r2 = r;
subarr(arr, &l1, &r1, &s1); //求左数组最大子数组
subarr(arr, &l2, &r2, &s2); //求右数组最大子数组
subcross(arr, &l, &m, &r, &s); //求跨中点最大子数组
//合并,先左再中后右,因为题目要求求第一个最大子数组,也就是最左边的一个
if(s1 >= s2 && s1 >= s){
*low = l1; *high = r1; *sum = s1;
}
else if(s >= s1 && s >= s2){
*low = l; *high = r; *sum = s;
}
else{
*low = l2; *high = r2; *sum = s2;
}
}
int main(){
int t, n, low, sum, high, i, j = 1;
scanf("%d", &t); //t表示测试的组数
while(t-- && scanf("%d", &n)){ //n表示每组测试的元素个数
i = 1; //用来标记下标,从1开始
while(n--)
scanf("%d", &a[i++]);
low = 1; //最左下标
high = i - 1; //最右下标
subarr(a, &low, &high, &sum);
printf("Case %d:\n%d %d %d\n", j++, sum, low, high);
if(t) putchar('\n');
}
return 0;
}