演算法小題 最大連續子串行和問題

2021-10-07 03:30:49 字數 3848 閱讀 5840

解法二解法三

解法三(番外版)

實驗結果

刷題的時候遇到了這個最大連續子串行和的問題,查閱研究了一下。記錄在此。希望能夠便人便己。

給定乙個陣列a[ n ] ,陣列元素均為自然數集(有正數,有負數),請求出該陣列乙個連續的子串行,使得這個子串行的和值最大,示例如下

a =

那麼它的最大連續子串行為 ,和值 = 23

說明:偷懶了一下,問題描述從其他部落格中cv一波。這裡要稍微注意一點,就是陣列內的元素可以全是負數,這樣的話最大的子串行和有的是返回0,有的是返回最大的負數。

好,問題很明確,廢話不多說,直接上解法。

最簡單的方式就是暴力解法。你不是讓求出最大的連續子串行和嗎?那我把所有的情況都遍歷一遍,不就行了。不動腦子,簡單粗暴,爽的一批。

那所有的情況又有哪些呢?

很容易想到,有可能是

a0,a0-a1,a0-a2,…,a0-a(n-1)

也有可能是

a1,a1-a2,a1-a3,…a1-a(n-1)

…簡單類推一下,就可以得出乙個o(n2)的解法。

不贅述多說,上**。

//暴力求解

intmaxsum

(int a,

int n)

return maximum;

}

簡單吧,是不是不敢相信自己的眼睛。但是,**行數雖然不多,但是其確實迴圈了兩層,有著o(n2)的複雜度。

好的,讓我們開動開動自己那聰明的大腦,想一想有沒有更好一些的方法。

嗯…啊…

突然間,靈光一閃。 你想到了這裡面可能有重複的子問題的性質呢(好吧,你沒想到,我也沒想到,我也是看了答案才想到的 ̄□ ̄||),能不能利用類似二分查詢那樣的方式,進行二分搜尋最大的子串行和呢?

像上面那樣,從中間截開分成兩段b和c,那麼我們可以得到下面這個結論:

最終的連續子串行

(1)、要麼在b中,

(2)、要麼在c中

(3)、要麼在包含中間分界線的兩個元素(這裡是6和-3)的乙個序列中。

最終確定是哪個序列,那就開看這三種情況下哪個更大了。

(搞不定?聽不懂?別啊,相信你,仔細看,畫個小圖慢慢想,you will get it。)

然後我們,在b和c中按照相同思路繼續遞迴即可。

好了,理論上完了,來點實踐的。

//遞迴演算法求解最大連續子串行和

intmaxsum1

(int data,

int beg,

int end)

else

int tmprigmax = inf;

tmpsum =0;

for(

int j = mid+

1; j<=end;

++j)

//判斷三種情況哪個更大些

int retsum = leftmax;

if(retsum

retsum = rightmax;

if(retsum

retsum = tmpleftmax+tmprigmax;

return retsum;

}}

注:很多線性表有關的題都有重複子問題的性質,都可以利用類似的遞迴演算法進行分解。本演算法是其中的一種,達到的時間複雜度是o(n*logn)

如果你想到了要求解的問題具有重複的子問題,那麼你有沒有想到這個問題中具有重疊的子問題,以及最優子結構呢? 如果這你都想到了,那麼,大神,留個方式唄,我想和你做朋友。

其實這裡是可以用動態規劃來做的。

其遞推關係式就是

*

*sum[i]

=max

(sum[i-1]

+data[i]

,data[i])*

*

其中,sum[i]表示序列結尾元素是第i個元素的最大序列和。比如說,

sum[5]代表序列結尾是a5的所有連續序列中和最大的那個值。有可能是

a0-a5,a1-a5,a2-a5,a3-a5,a4-a5,a5 。

別問我是怎麼想到這個遞推公式的,***。 但是,我用了我的很多的腦細胞模模糊糊想出了一點東西,與君共勉。

對於大多數問題來說,我們的求解思路只要考慮到所有的解空間就可以(當然不一定需要每個都比較,否則就變成了暴力求解了)。

對於這題來說,答案肯定是a[i]-a[j] 的某個序列,不用想,j可以是0-n-1。

所以,我把j=0的序列最大值求出來,然後j=1的序列最大值求出來,…,然後在遍歷一次,最終就可以確定出整體的最優值。

從某種程度上來說,我的上述分解空間的想法有點像先確定乙個條件,把符合條件所有的情況都算出來,然後在看有多少個這樣的條件,進而分解全部的空間。在實際中,這種思路也有不少應用。

好了,說了這麼多,下面該來點硬貨了。

//動態規劃求解: sum[i]=max(sum[i-1]+data[i],data[i])

intmaxsum

(int data,

int n)

int max = sum[0]

;for

(int i =

1;i++i)

return max;

}

我的小夥伴,你的大腦累了嗎? 累了,好咱來玩個遊戲。

想象一下,你在一條放著金幣和陷阱的公路上行走。沒進過乙個金幣站點,你就可以獲得對應的金幣,沒經過乙個陷阱站點,你就必須損失相應的金幣數量。 你可以選擇從任意乙個站點開始,也可選擇在任意乙個站點結束。但是,在開始和結束的站點之間的這段連續站點內,必須一直走下去。

ps:遊戲你可以重玩多次,這樣的話,你就可以記錄下上一次的發現成果,如下圖所示,陷阱我已經給你圈出來了哦。

好吧,夥伴,你會怎麼玩?

反正我是這樣玩的:

我手裡拿著個小本本,在玩的過程中一直記錄兩個值,乙個是到目前為止獲得的最大金幣數量a,乙個是到當前位置還剩餘的金幣數量b。注意這兩個值不一樣,前者是所以連續序列段的最大的金幣數量,後者是從乙個序列的開始到目前的位置這段連續的站點所獲得的金幣數量,這是指的是當前連續序列段。

出發之前,我悟到了乙個遊戲策略:選好每個連續序列的起點。

我是怎麼選的呢,我就放心大膽的往前走,直到我當前還剩餘的金幣數量b小於0了,那麼我就需要重新開啟乙個新的連續序列了。(遇到陷阱不可怕,只要我當前的金幣數量夠支付陷阱的,那我就可能在後面遇到更大的金幣)當然,在整個過程中,我需要記錄最大的金幣數量a。

ok。如果不出意外的話,我就可以在o(n)的時間內獲得最大的連續序列和。

好了,遊戲玩完了,下面改上實際的**了。

//番外動態規劃

intmaxsum2

(int data,

int n)

else}if

(allmax == inf)

//如果全部是負數的話,返回0,(或者重新遍歷,返回最大負數)

allmax =0;

return allmax;

}

注:

(1)、當然:這裡有點小瑕疵,就是如果輸入全部是負數的話,返回的是0,而不是最小的負數。

(2)、本程式時間上的複雜度就是o(n)

(3)、本質上,這裡面用的還是動態規劃的思想。不過,只是出發點比較稍微形象店。

好了,最終的實驗 結果如下(分別是後三種答案,第一種就不測試了):

最大連續子串行和 問題

為了證書,重新開始刷資料結構,原本也就是學的一知半解的,上來就給我來了乙個最大子串行和問題,搞得我很無奈,貌似hdu1003就是這個問題.第一 重新定義乙個新的sum陣列,然後進行儲存連續子串行的和,遇到前面的加和sum i 1 小於0,就直接讓sum i a i include include i...

最大連續子串行和問題

第一次看 資料結構與演算法分析 c語言描述 這本書的時候,被書中一上來就給的最大子串行和問題給直接鎮住了。直觀感覺就是好難,好牛逼。問題描述 給定整數k1,k2,k3,kn,求從第i個數到第j個數的最大值。如果所有整數均為負數,那麼最大子串行和規定為0 根據題目描述,最直接的演算法就是窮舉所有的從i...

問題 A 最大連續子串行

問題 a 最大連續子串行 命題人 外部匯入 時間限制 1.000 sec 記憶體限制 32 mb 解決 493提交 1082統計 題目描述 給定k個整數的序列,其任意連續子串行可表示為,其中 1 i j k。最大連續子串行是所有連續子串行中元素和最大的乙個,例如給定序列,其最大連續子串行為,最大和為...