連續子串的最大值 經典的DP問題

2021-07-04 12:10:46 字數 3792 閱讀 1869

【問題描述】

在長度為n的整形陣列中,求連續子串的和的最大值,要求複雜度為o(n)。

例如:1 2 3 -1 -20 100 34,結果為134。

【分析】

[思路一]

自己最初的想法,利用回溯的思想,從下標為0的元素開始遍歷,用former儲存當前最大的連續子集的和,當sum小於0的時候開始回溯,即刪除這個元素,刪除到已遍歷陣列最右邊的那個正整數為止(用下標j標識)。事後覺得這個演算法比較複雜,因為要維護下標j的指向,以及還要單獨求出陣列中的最大值,並最後與former進行比較。因此這個演算法只是實現了功能,此演算法的效率和**量都不好。

#include 

#include 

intmaxadjval(

inta, 

intlen)  

a[i]>0 ? flag=true

: null;  

sum+=a[i];  

if(sum<=0)  

if(former

sum=0;// reset

}  }  

return

maxval>0 ? (sum>former? sum:former) : maxval;  

}  int

main()  

, i=0, res=0;  

printf("enter some numbers (ctrl+z to end):/n"

);  

while

((1==scanf(

"%d"

,&a[i])) && ++i<100);  

rewind(stdin);  

res=maxadjval(a,i);  

printf("%d/n"

,res);  

return

0;  

}  

[思路二]

思路二對演算法進行了簡化,不採用回溯,只用maxsum儲存當前最大的連續子集的和,如果陣列全為負數,找乙個最大值就可以了。注意當cursum小於0的時候,要將cursum置為0。

乙個無序的陣列,找出相鄰的任意個元素,使得其和最大。要得o(n)的複雜度,順序讀取,任意步驟都是當前讀取的序列判斷最優解,此謂聯機演算法。(參考: )

用vector可以描述如下:

intmaxsubsum(

const

vector<

int>& vec)  

return

maxsum;  

}  

用c語言描述如下:

#include 

#include 

intmaxadjval(

inta, 

intlen)  

return

max;  

}  int

main()  

, i=0, res=0;  

printf("enter some numbers (ctrl+z to end):/n"

);  

while

((1==scanf(

"%d"

,&a[i])) && ++i<100);  

rewind(stdin);  

res=maxadjval(a,i);  

printf("%d/n"

,res);  

return

0;  

}  

【問題擴充套件】

1. 問這樣的子串有多少個。

2. 如果是首尾相連的,那麼最大子串和是多少,有多少個。

3. 如果是首尾相連的,取兩個不相交子串,那麼最大子串和是多少。

4. 若存在多個最大子串和,則輸出第乙個最大子串和的起始和終止下標。

【參考】

(1) 經典的dp問題。

請參考這道acm題:

#include 

using

namespace

std;  

void

maxsub(

int*arr, 

intcount, 

int&first, 

int&last, 

int∑);  

intmain()  

return

0;  

}  //求最大子串行之和,arr是原來的陣列,count是陣列的元素個數,

//first,last是所求子串行的第乙個元素,最後乙個元素的下標,sum是所求子串行的元素之和

void

maxsub(

int*arr, 

intcount, 

int&first, 

int&last, 

int∑)  

//如果臨時的最大值大於實際的最大值,則更新子串行的first, last, sum

if(sum < tsum)  

}  }  

(2) 一網友的幾種解決方法:

#include

using

namespace

std;  

#define num 10

template

<

class

elemtype>  

//普通2層迴圈時間複雜度 o(n*n)

void

maxsum(elemtype*p,

int& beg,

int& end,

int& sum)  

}  }  }  

//利用分治演算法時間複雜度o(n)

template

<

class

elemtype>  

void

maxsum1(elemtype*p,

intlow,

inthigh,

int& beg,

int& end,elemtype& sum)  

else

}  elemtype sumr=0; //跨越左右區間的右邊部分的最大子段和

elemtype cursum1=0; //當前跨越左右區間的右邊區間部分的子段和

for(i=mid+1;i<=high;i++)  

}  sum=suml+sumr; //跨越左右區間的最大欄位和

if(sum<=leftsum)   

if(sum<=rightsum)   

}  }  //利用動態規劃時間複雜度也為o(n)

template

<

class

elemtype>  

void

maxsum2(elemtype*p,

int& beg,

int& end,elemtype& sum)  

else

if(b>sum) 

//判斷當前子段和,之前最大子段和的大小

}  }  void

main()  

;  int

beg,end,sum;  

beg=end=sum=0;  

// maxsum(p,beg,end,sum);

// maxsum1(p,0,num-1,beg,end,sum);

maxsum2>(p,beg,end,sum);  

cout<<"開始下標為:"

<

cout<<"結束下標為:"

<

cout<<"最大欄位和為:"

<

}  

(3) 參考《程式設計之美》

(4) 關於此問題的討論。

DP 多段 「連續子段和」 的最大值問題

給一段連續的序列s1,s2,s3,s4 sx,sn 1 x n 1,000,000,32768 sx 32767 我們定義了sum i,j si sj 1 i j n 現在求的是 sum i1,j1 sum i2,j2 sum i3,j3 sum im,jm maximal ix iy jx or ...

求連續子串行的最大值

問題描述 有一串數字 可正可負的int,放在陣列num裡 要求找到起始位置start和終止位置end,使得從start位置到end位置的所有數字之和最大,返回這個最大值max。演算法思想 使用動態規劃。設 f x 為以 a x 終止且包含 a x 的最大序列的和,有 f 1 a 1 f x 1 f ...

面試經典(7) 連續子陣列的最大值

題目 輸入乙個整型陣列,陣列有整數也有負數,求其子陣列的最大值。演算法分析 微軟經典的一道題目。維持乙個當前累加的和cursum,如果cursum 0,那麼需要更改cursum為data i 這個很好理解,因為當前累加和是負數,是乙個累贅。如果cursum 0,那麼只需繼續累加data i 每次更改...