问题描述:最大连续序列问题。第一行输入要解决的case数,接下来每一行的第一个是序列的元素个数,后面跟着元素;输出最大和与序列号。
2 5 6 -1 5 4 -7 7 0 6 -1 1 -6 7 -5
Case 1: 14 1 4 Case 2: 7 1 6
本题有多种解法:
1、暴力破解。
2、分治策略。(我用的是分治策略,参考的是算法导论。结果对了,但是始终ac不过。)
#include<stdio.h>
#include<string.h>
typedef struct {
int sum;
int low,high;
}Max;
Max FindMaxSum(int num[],int low, int high);
Max FindCrossingMaxSum(int *num,int low,int high);
void main(){
int **num;
int T,i,j=0,c,sum=0,temp;
Max m;
scanf("%d",&T);
num=(int **)malloc(sizeof(int)*T);
for(i=0;i<T;i++){
c=0;
j=0;
scanf("%d",&temp);
num[i]=(int *)malloc(sizeof(int)*(temp+1));
num[i][j++] = temp;
while(c != '\n'){
scanf("%d",&temp);
num[i][j++] = temp;
c = getchar();
}
}
for(i=0;i<T;i++){
m = FindMaxSum( num[i], 1, num[i][0]);
printf("Case %d:\n%d %d %d\n", i+1, m.sum, m.low, m.high);
if(i!=T-1)
printf("\n");
}
}
Max FindMaxSum(int *num,int low, int high){
if(high == low){
Max m;
m.sum = num[low];
m.low = m.high = low;
return m;
}
else{
int mid = (low + high) / 2;
Max left, right, crossing;
left = FindMaxSum(num, low, mid);
right = FindMaxSum(num, mid+1, high);
crossing = FindCrossingMaxSum(num, low, high);
if(crossing.sum>=left.sum && crossing.sum>=right.sum)
return crossing;
else if(left.sum>=right.sum && left.sum >= crossing.sum)
return left;
else
return right;
}
}
Max FindCrossingMaxSum(int *num,int low,int high){
int i,sum=0,MaxLeftSum,MaxRighSum;
Max crossing;
for(i=(low + high)/2; i>=low; i--){
sum += num[i];
if(sum>=MaxLeftSum){
MaxLeftSum = sum;
crossing.low = i;
}
}
sum = 0;
for(i=(low + high)/2+1; i<=high; i++){
sum += num[i];
if(sum>=MaxRighSum){
MaxRighSum = sum;
crossing.high = i;
}
}
crossing.sum = MaxRighSum + MaxLeftSum;
return crossing;
}
备忘:
(1)动态分配多维数组;
(2)二分算法的递归实现
(3)结构体的应用
3、动态规划。(最优解,仅转载算法思想,)
用数组a表示存储的数字序列,sum表示当前子段和,maxsum表示最大子段和。不妨设想:当sum为负数的时候:
1.当下一个数字a[i]为正数的时候,sum+a[i] < a[i],不如将sum归零重新计算
2.当下一个数字为负数的时候,sum+a[i]< 0 ,若再下一个数字还为负数,依旧可以得出和小于零……直到遇到一个正数,此时回到1的情况,不如将sum归零计算。
综上所述,当sum为负数的时候,归零。
那么再看sum为正数的时候:
1.当下一个数字a[i]为正数的时候,当然选择加上a[i],并且可以更新maxsunm;
2.当下一个数字a[i]为负数的时候,由于不知道后面数字的情况,无法做出决策。
综上所述,当sum>maxsum的时候,要更新maxsum,并且一直累加a[i]。
题目还要求输出这个子段的start位置和end位置。可以用x,y分别表示当前最优(大)的子段的开始和结束位置,然后再用sta和ed变量表示当前子段的开始和结束位置。结合上面的叙述:
1.当sum>maxsum的时候,即需要更新的时候,就要更新x和y的位置;
2.当sum< 0的时候,即需要使sum归零计算的时候,就需要把sta的位置置为i+1(指向下一个位置的数字);