最大子段和問題

給定的n個整數(可以是負數)的序列啊a[1..n],求連續的的子段中和最大的值,並給出子段的啓始下表和結束下標。

注意:子段中可能包含負數,但是必須是連續的,中間不可斷開

若不允許負數,則只要計算連續正數中和最大的,複雜性較原來低

若可以見斷,則問題只要找出數組中全部是正數的數之和,問題更加簡單

 

原問題的求解方法

一、窮舉遍歷法

該方法直接列舉n個數種不同的子段個數,並求和,故有n*n種,時間複雜性爲O(n*n)

需要注意的是,在求字段和的過程貌似需要再次循環,但是通過技巧,可以省略,在窮舉子段的同時求和並比較大小(找最大的,編程的基本功)

代碼如下:

int MaxSum(int n,int *a,int &besti,int &bestj)
{
 int sum=0;
 for(int i=0;i<n;i++)
  for(int j=i;j<n;j++)
  {
   int subsum=0;
   for(int k=i;k<=j;k++)
    subsum+=a[k];
   if(sum<subsum)
   {
    sum=subsum;
    besti=i;
    bestj=j;
   }
  }
  return sum;
}

//循環窮舉改寫
int MaxSum(int n,int *a,int besti,int bestj)
{
 int sum=0;
 for(int i=0;i<n;i++)
 {
  int subsum=0;
  for(int j=i;j<n;j++)
  {
   subsum+=a[j];
   if(sum<subsum)
   {
    sum=subsum;
    besti=i;
    bestj=j;
   }
  }

 }
}

 

 

方法二:分治法

該方法的關鍵是找到分治的中間點,以及如何將分治後的結果比較拼接爲最終答案

取中間數據,遞歸求出左右的最大值,然後將在左右兩邊的,即包含中間節點的最大值求出,分別與左右的遞歸值比較,其中最大的爲結果。

注意:遞歸的出口1,左右相等,並排除掉負數,即含負數的值爲0‘

            遞歸的出口2,左右不等,在得到左右相等的結果後,比較左右兩邊的包含中間的最大值,必須做一個加法操作,並做判斷(排除掉中間值比較小的部分),其中,如果有負值,則取0;

遞歸的過程是不斷往下求解,最終通過最最簡單的問題的答案網上走,得出原問題的答案。遞歸樹是其中的關鍵以及程序的調用形式

int MaxSubSum(int *a,int left,int right)
{
 int sum=0;
 if(left==right)
  sum=a[left]>0?a[left]:0;
 else
 {
  int center=(left+right)/2;
  int leftsum=MaxSubSum(a,left,center);
  int rightsum=MaxSubSum(a,center+1,right);
  int s1=0;
  int lefts=0;
  for(int i=center;i>=left;i--)
  {
   lefts+=a[i];
   if(lefts>s1)
    s1=lefts;
  }
  int s2=0;
  int rights=0;
  for(int j=center+1;j<=right;j++)
  {
   rights+=a[j];
   if(rights>s2)
    s2=rights;
  }
  sum=s1+s2;
  if(sum<leftsum)sum=leftsum;
  if(sum<rightsum)sum=rightsum;
 }
 return sum;
}

int MaxSum(int n,int *a)
{
 return MaxSubSum(a,0,n-1);
}

 

方法三、動態規劃方法

該方法的本質是通過對變量數據結構的定義來表示求解問題中重複部分,也就是通過數學上的規律關係,簡化問題的計算,注意,只是簡化了問題的求解方法。簡單的關係式,甚至一個變量,但是包含的是數學上的規律,簡單即複雜。

最大子段和問題設置數組b[j]:表示以b[j]結尾的必須包含b[j]的子段中和最大,求出所有的b[j]後,找出b[1...n]中最大的值極爲最大子段和。

設置b[j]的原因是b[j]很好求,如果b[j-1]<0;則b[j]=a[j];否則b[j]=b[j-1]+a[j];

b[1..n]中肯定包含a[1..n]的最大子段和,因爲最大字段和是其中的一個特例,極最大值。

這些主要是一個數學求和公式找出求和中間的最大值的數學公式化簡而來。

程序中給出了最大子段和的啓始位置和結束位置,因爲只要最大的值,爲了簡化存儲空間,中間的計算結果全部平拋棄,只保留最大值,所以該方法空間和時間都達到了巨大的優化,這也是數學的魅力所在

 

 

int MaxSum(int n,int *a,int &bestj,int &besti)
{
 int sum=0,b=0;
 for(int i=0;i<n;i++)
 {
  if(b>0)
   b+=a[i];
  else
   b=a[i];
  if(sum<b)
  {
   sum=b;
   besti=i;
  }
 }
 
 if(a[besti]==sum)
 {
  bestj=besti;
 }
 else
 {
  int sum1=sum;
  for(int j=besti;sum1>0;j--)
  {
   sum1-=a[j];
  }
  bestj=j+1;
 }
 return sum;
}

發佈了27 篇原創文章 · 獲贊 10 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章