基礎演算法--B最大欄位和
Description
Given a sequencea[1],a[2],a[3]......a[n], your job is to calculate the max sum of asub-sequence. For example, given (6,-1,5,4,-7), the max sum in this sequence is6 + (-1) + 5 + 4 = 14.
Input
The first lineof the input contains an integer T(1<=T<=20) which means the number oftest cases. Then T lines follow, each line starts with a numberN(1<=N<=100000), then N integers followed(all the integers are between-1000 and 1000).
Output
For each testcase, you should output two lines. The first line is "Case #:", #means the number of the test case. The second line contains three integers, theMax Sum in the sequence, the start position of the sub-sequence, the endposition of the sub-sequence. If there are more than one result, output thefirst one. Output a blank line between two cases.
Sample Input
2
5 6 -1 5 4 -7
7 0 6 -1 1 -6 7-5
Sample Output
Case 1:
14 1 4
Case 2:
7 1 6
最大欄位和一直都有很多做法:
1. 列舉演算法
簡單的列舉演算法當然可以解決這個問題,列舉所有可能的起始下標(i=1,2,3……,n)和終止下標(j=1,2,3……,n),累加各種情況的元素和,並從中選出最大的欄位和。
2. 二分策略演算法
如果將所給的序列a[1:n]分為長度相等的兩段a[1: (n/2)]]和b[(n/2)+1:n],分別求出這兩段的最大欄位和,則a[1:n]的最大欄位和有3種情形。
(1),a[1:n]的最大子段和與a[1: (n/2)]的最大子段和相同;
(2),a[1:n] 的最大子段和與a[(n/2)+1:n]的最大子段和相同;
(3),a[1:n] 的最大子段和為a[i:j],()
情形(1)和(2)可遞迴求得。
對於情形(3),序列中的元素a[(n/2)]與a[(n/2)+1]一定在最大子段中。因此可以算出a[i: (n/2)]的最大值S1;並計算出a[(n/2)+1:j]中的最大值S2。則S1+S2即為出現情況(3)時的最優值。
據此可設計最大子段和的分治演算法求出最大子段。由於子問題不獨立,不同於一般的二分治演算法,這裡演算法的實質是“三”分治。
intmax_sum3(int a[],int n)
{
return max_sub_sum(a,1,n);
}
intmax_sub_sum(int a[],int left,int right)
{
intcenter,i,j,sum,left_sum,right_sum,s1,s2,lefts,rights;
if(left==right)
if(a[left]>0)
return a[left];
else
return 0;
else
{
center=(left+right)/2;//二分中心位置
left_sum=max_sub_sum(a,left,center);//求左部最大子段和
right_sum=max_sub_sum(a,center+1,right);//求右部最大子段和
s1=0;//處理情形3
lefts=0;
for(i=center;i>=left;i--)
{
lefts=lefts+a[i];
if(lefts>s1)
s1=lefts;
}
s2=0;
rights=0;
for(i=center+1;i<=right;i++)
{
rights=rights+a[i];
if(rights>s2)
s2=rights;
}
if(s1+s2<left_sum&&right_sum<left_sum)//情形1
return left_sum;
if(s1+s2<right_sum)//情形2
return right_sum;
return s1+s2;//情形3
}
}
3. 動態規劃演算法
方法2中由於分解後子問題不獨立,情形3中重複計算較多,沒有充分利用前期的計算結果。而動態規劃的特長就是解決分解的子問題不獨立的情況。用動態規劃就是通過開闢儲存空間,儲存各個子問題的計算結果,從而避免重複計算。並且動態規劃有很強的階段遞推思想,用前一階段儲存的計算結果遞推後一階段的結果。
記sum[i]為a[1]~a[i]的最大子段和,記this_sum[i]為當前子段和。
this_sum[i]從i=1開始計算,當this_sum[i-1]>=0時,前面子段的和對總和有作用,所以要累加當前元素的值;當this_sum[i-1]<=0時,則開始重新累加。
初值:this_sum[0]=0;i=1,2……,n時
this_sum[i]=this_sum[i-1]+a[i] 當this_sum[i-1]>=0
this_sum[i]=a[i] 當this_sum[i-1]<0
相應的sum[i]的遞推定義式即:a[0]=0,i=1,2……,n時
sum[i]=sum[i-1] 當this_sum[i]<=sum[i-1]
sum[i]=this_sum[i] 當this_sum[i]>sum[i-1]
sum[i]在記錄a[1]~a[i]的最大子段和。
由此思想得出的完全程式碼:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int a[100001];
int maxSub(int *a,int n) //求最大子段和函式
{
int i=0, max=0,max_pos=0;
int si_1=0,si=0;
int *p = (int *)malloc(n*sizeof(int));//通過p來記錄this_sum[i-1]的狀態
if (p==NULL)
return -1;
max =si_1 = a[0]; //初始化max,sum[i-1]
p[0] = 0; //p[i]=0表示this_sum[i-1]<0
for (i=1; i<n; i++)
{
if (si_1<0){ //sum[i-1]<0的情況
p[i] = 0;
si = a[i]; //si即sum[i]
} else{
p[i] = 1;
si = si_1+a[i]; //累加
}
si_1 = si;
if (si>max){ //記錄最大子段和
max = si;
max_pos = i; //記錄最大子段終點
}
}
for (i=max_pos; i>=0; i--)
if (p[i]==0) //尋找最大欄位起點
break;
printf("%d %d %d\n", max,i+1,max_pos+1);
free(p);
p= NULL;
return max;
}
int main()
{
int n,i,m,j;
scanf("%d",&m);
getchar();
for(i=0;i<m;i++)
{
scanf("%d",&n);
for(j=0;j<n;j++)
scanf("%d",&a[j]);//輸入數列
printf("Case%d:\n",i+1);
maxSub(a, n);
if((i!=m-1))
printf("\n");
memset(a,0,sizeof(a));
}
return 0;
}