日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

最大子序列求和_最大子序列和问题

發布時間:2025/3/21 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 最大子序列求和_最大子序列和问题 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

問題描述:

給定一個整數序列,a0, a1, a2, …… , an(項可以為負數),求其中最大的子序列和。如果所有整數都是負數,那么最大子序列和為0;

例如:對于序列-2, 11, -4, 13, -5, –2。 所求的最大子序列和為20(從11到13,即從a1到a3)。

用于測試下面代碼的的主函數代碼如下:(注意要更改調用的函數名)

int main(int argc, char **argv)

{

vector a;

a.push_back(-2);

a.push_back(11);

a.push_back(-4);

a.push_back(13);

a.push_back(-5);

a.push_back(-2);

int result;

result = maxSubSum1(a); //在這里更改調用的函數名 cout<

}

方法一:對所有的子序列求和,在其中找到最大的

這是最容易想到的,也是最直接的,就是對所有的子序列求和,代碼如下:

int maxSubSum1( const vector &a )

{

int maxSum = 0;

for(int i=0; i

{

for(int j=i; j

{

int thisSum =0;

for( int k=i; k<=j; k++ )

{

thisSum += a[k];

}

if(thisSum>maxSum)

{

maxSum = thisSum;

}

}

}

return maxSum;

}

分析:

① 三層循環嵌套,時間復雜度為O(n^3)

② 包含有大量的重復計算,例如i=1時: 當 j=3,則計算a1+a2+a3;j=4,則計算a1+a2+a3+a4;其中a1+a2+a3是重復計算的。

另一種思路:上面的方法在每一次循環中,固定i,并把a[i]當做起點,下面的方法將a[i]當做終點。

int maxSubSum1_2( const vector &a )

{

int maxSum = 0;

for(int i=0; i

{

for(int j=0; j

{

int thisSum =0;

for(int k=j; k<=i; k++)

{

thisSum +=a[k]; //從節點j開始 累加到節點i

if(thisSum>maxSum)

{

maxSum = thisSum;

}

}

}

}

return maxSum;

}

方法二:從某點開始的所有序列中,找最大的

如果你意識到,子序列總要有一個位置開始,那么變換一下循環方式,只要求出在所有位置開始的子序列,找到最大的。代碼如下:

int maxSubSum2( const vector &a )

{

int maxSum = 0;

for(int i=0; i

{

int thisSum =0;

for(int j=i; j

{

thisSum +=a[j]; //從節點i開始 累加到結尾

if(thisSum>maxSum)

{

maxSum = thisSum;

}

}

}

return maxSum;

}

分析:

① 兩層循環嵌套,時間復雜度為O(n^2)

② 雖然比方法一要少,但同樣包含重復計算。例如:當i=1時,要計算a1+a2+a3+a4+……;當i=2時,要計算a2+a3+a4+……;其中a2+a3+a4+……是重復的。

注意:下面是一個錯誤的方法,因為它的起始點固定了,每次都從a0開始,是不能保證遍歷所有的子序列的。

int maxSubSum2_2( const vector &a )

{

int maxSum = 0;

for(int i=0; i

{

int thisSum =0;

for(int j=0; j<=i; j++ )

{

thisSum +=a[j]; //從節點0開始 累加到節點i

if(thisSum>maxSum)

{

maxSum = thisSum;

}

}

}

return maxSum;

}

如果希望將固定終點,那么計算的時候就要從終點開始,依次往前累加。代碼如下:

int maxSubSum2_3( const vector &a )

{

int maxSum = 0;

for(int i=0; i

{

int thisSum =0;

for(int j=i; j>=0; j-- )

{//從節點i開始,向前累加到結尾0

thisSum +=a[j];

if(thisSum>maxSum)

{

maxSum = thisSum;

}

}

}

return maxSum;

}

方法三:從某一個正數開始

第一步:

到目前為止,題目的信息你只用到了:“最小子序列之和為0”(若有一項大于0,那么子序列的和一定大于或等于該項,也就大于0;因為若所有項都是負數,那么結果為0

如果你再挖掘一下題意:你就會發現,如果a[i]是負的,那么a[i]一定不是最終所有結果子序列的起始點。代碼可以改造為:

int maxSubSum3_1( const vector &a )

{

int maxSum = 0;

for(int i=0; i

{ //(相對方法2,新增)如果a[i]<=0,那么a[i]一定不是所要求的起點,所以直接跳過去(利用for循環中有i++)

if( a[i]<=0 )

continue;

int thisSum =0;

for(int j=i; j

{

thisSum +=a[j];

if(thisSum>maxSum)

{

maxSum = thisSum;

}

}

}

return maxSum;

}

第二步:

如果你又再一步發現:任何負的子序列,不可能作為最優子序列的前綴

又因為上一步已經保證,序列以正數開頭a[i]>0,所以若a[i]到a[j]之間元素的序列和 thisSum<=0時,則i+1和j之間元素不會為最優子序列的前綴,可以讓i=j,即不需要判斷在i和j之間元素開頭。代碼如下

int maxSubSum3_2( const vector &a )

{

int maxSum = 0;

for(int i=0; i

{ //(相對方法2,新增)如果a[i]<=0,那么a[i]一定不是所要求的起點,所以直接跳過去(利用for循環中有i++)

if( a[i]<=0 )

continue;

int thisSum =0;

for(int j=i; j

{

thisSum +=a[j];

if(thisSum>maxSum)

{

maxSum = thisSum;

}

else if( thisSum <= 0 )

{ //(相對方法3_1 新添) thisSum = 0; i = j; }

}

}

return maxSum;

}

第三步:

如果你又進一步發現:因為要求序列開始元素大于0, 若以a[i]開頭的序列,a[i]>0,那么可以知道,所求的最終子序列一定不會以a[i+1]開始, 因為若到相同的元素終止,那么從a[i]開始序列,一定大于從a[i+1]開始的序列。因為s[i, k]=s[i+1, k]+a[i] 例如:a1+a2+a3>0,而又由于這時a1>0,

那么所求子序列一定不會以a2開始,因為從a1開始會更大。 更進一步,如若一個子序列thisSum>0(其中thisSum是從第m項到第n項的和),那么序列一定不會以a[m]和a[n]之間的項開始。 因為一直thisSum第一個元素a[m]是大于0的,且以a[m]開始的所有子序列都是大于0的,因為若存在子序列小于0,就會提前返回了。 例如:若程序執行到thisSum (為a2+a3+a4),若thisSum>0,則能說明,a2>0,并且a2+a3>0, a2+a3+a4>0。那么 也可以跳過a[m]和a[n]之間的項,即另

i=j。

int maxSubSum3_3( const vector &a )

{

int maxSum = 0;

for(int i=0; i

{ //(相對方法2,新增)如果a[i]<=0,那么a[i]一定不是所要求的起點,所以直接跳過去(利用for循環中有i++)

if( a[i]<=0 )

continue;

int thisSum =0;

for(int j=i; j

{

thisSum +=a[j];

if( thisSum>0 )

{ //(相對方法3_2 新添)

if(thisSum>maxSum)

{ maxSum = thisSum; }

i = j; //(相對方法3_2 新添)

}

else if( thisSum <= 0 )

{ //(相對方法3_1 新添)

thisSum = 0;

i = j;

}

}

}

return maxSum;

}

第四步:

最后如果你又了解一些程序結構上的優化的知識,那么你會發現下面的問題:

① 循環的分支可以改變一下,去除嵌套分支結構。

② 判斷語句的分支中有共同部分,( i=j ),可以抽取出來。

以上兩步以后,循環部分的代碼編程 變成:

for(int i=0; i

{

if( a[i]<=0 )

continue;

int thisSum =0;

for(int j=i; j

{

thisSum +=a[j];

if(thisSum>maxSum)

{ maxSum = thisSum; }

else if( thisSum>0 )

{ //do nothing }

else if( thisSum <= 0 )

{ thisSum = 0; }

i = j;

}

}

③ 下面這步非常重要,如果你發現,內層循環的循環變量j 和 外層循環的循環變量i同步增長,那么你是否能夠想到,外層循環可能沒有存在的必要。在這里到底能不能去除外層循環,取決于外層循環中是否有額外的工作要做。這里的額外工作是是if(a[i]

<=0) 判斷語句,如果你能發現內層循環的 if(thisSum < 0)的判斷能夠替代 if( a[i]<=0 ) 的工作。因為thisSum是由a[j]得到的。

到這里,代碼就可以神奇的變為如下的形式:常量空間,線性時間

int maxSubSum3_4( const vector &a )

{

int maxSum = 0;

int thisSum = 0;

for(int j=0; j

{

thisSum += a[j];

if(thisSum>maxSum)

{ maxSum = thisSum; }

else if( thisSum>0 )

{ //do nothing }

else if( thisSum < 0 )

{ thisSum = 0; }

}

return maxSum;

}

分析:

① 只有一層循環,時間復雜度為O(n)常量空間,線性時間,這是最優解法

分享到:

2012-09-07 21:52

瀏覽 348

評論

總結

以上是生活随笔為你收集整理的最大子序列求和_最大子序列和问题的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。