1. 程式人生 > 其它 >最大子段和之分治法

最大子段和之分治法

技術標籤:演算法題資料結構分治演算法演算法

最大子段和之C語言

問題描述:
給定一個數組,找出其中可以構成最大數的子段,需要注意的是,這個不同於最大子序列求和
—— 最大欄位求和:欄位必須是連續的
—— 最大子序列求和:子序列只要是包含在原來的序列中即可
舉個例子:

-1 4 -3 1 5 -1 4 -5 2

求上述的陣列中的最大欄位和,不難得知,最大子段和就是 10 ,也就是子段 4 -3 1 5 -1 4

思路:
首先,列舉?沒有列舉解決不了的問題好吧。但是真的列舉麼?在時間和空間複雜度上都會有很大的消耗;
分治?該怎麼分治呢?由於在一個子段中,分治的話必須將原來的陣列劃分成幾個部分,在本題中,大致有三種情況:

  1. 當最大子段和在所選的界定值左邊的時候
  2. 當最大欄位和在所選的界定值右邊的時候
  3. 當最大子段和包含所選的界定值,也就是在界定值的兩側的時候

在第一種情況下,當最大子段和位於陣列最左邊的時候,通過不斷地遞迴,保留最大的子段和,最後相加便可得到
同樣,在第二種情況下,也就是說最大子段和在右邊的時候,類似與上面的,通過不斷地遞迴,相加可以求得
第三種情況相對來說比較複雜,當界定值包含在最大子段和中的時候,看上去就是類似於與問題了,但是,根據上述的想法,取得的界定值往左右兩邊分別尋找,相加便可以求得

在每一個劃分的區間,都是採用上述三個步驟,可以得到 MAX Sum

程式碼演示:

#include<stdio.h>
#include<stdlib.h> #define n 5 int a[n]; int MaxSum(int a[],int l,int r, int *sitel, int *siter) { int c; //中間位置 int lsum,rsum,csum; //左邊、右邊、中間最大和 if(l==r) { *sitel=l; *siter=l; return a[l]; } else { c=(l+r)/2; //遞迴求左右兩邊的最大欄位和 lsum=
MaxSum(a,l,c, sitel, siter); //左邊最大欄位和 //左邊大時的位置臨時儲存 int ltemp_sitel, ltemp_siter; ltemp_sitel=*sitel; ltemp_siter=*siter; rsum=MaxSum(a,c+1,r, sitel, siter); //右邊最大欄位和 //右邊大時的位置臨時儲存 int rtemp_sitel, rtemp_siter; rtemp_sitel=*sitel; rtemp_siter=*siter; //計算中間最大欄位和 //求左半部份 int i; int csuml=0,cleft=0; int templ=c; for(i=c;i>=l;i--) { cleft=cleft+a[i]; if(cleft>csuml){ csuml=cleft; templ=i; } } //求右半部份 int j; int csumr=0,cright=0; int tempr=c; for(j=c+1;j<=r;j++) { cright=cright+a[j]; if(cright>csumr){ csumr=cright; tempr=j; } } //中間最大和 csum=csuml+csumr; //位置確定 if(csum>lsum) if(csum>rsum){ //中間最大 *sitel=templ; *siter=tempr; return csum; }else{ //右邊最大 *sitel=rtemp_sitel; *siter=rtemp_siter; return rsum; }else if(lsum>rsum){ //左邊最大 *sitel=ltemp_sitel; *siter=ltemp_siter; return lsum; } } } void input() { int i; printf("請輸入一組數字:"); for(i=0;i<n;i++) scanf("%d",&a[i]); } void main() { int Sum; int sitel=0; int siter=n-1; int i; input(); Sum=MaxSum(a,0,n-1,&sitel, &siter); printf("最大和為:%4d\n",Sum); printf("構成最大和的數值:"); for(i=sitel; i<=siter; i++) printf("%4d",a[i]); }

結果如下所示:
在這裡插入圖片描述
小結:
在分治法解決問題的時候,不斷地拆分問題,將問題拆分成我們可以解決的問題即可,類似於上題,將問題最後拆分成最小的子集,可以直接判斷最大子段和
遞迴的思想還是很重要!!!