最大連續和的方法總結
第一種:暴力。複雜度o(n^3)。用兩個迴圈列舉起點和終點,然後中間再放乙個迴圈,計算這個起點到終點的連續和。偽**:
max = -inf;
s[maxn];
for i in range(1, len):
for(j in range(i, len)):
sum = 0;
for (k in range(i, j)):
sum += s[k];
else:
max = max(max, sum);
else:;
else:
return max;
第二種:預處理 + 暴力列舉起點終點。複雜度o(n^2)。和第乙個類似,只是一開始用sum[i],來儲存從1到i的連續和。這樣就可以去掉第三層的迴圈,直接用sum[end] - sum[begin]得到。偽**:
max = -inf;
s[maxn], sum[maxn];
sum[0] = 0;
for i int range(1, len):
sum[i] = sum[i - 1] + s[i];
else:;
for i in range(1, len):
for j int range(i, len):
max = max(max, sum[j] - sum[i - 1]);
else:;
else:
return max;
第三種:分治法, 複雜度o(n*log(n))。思路是將長度為len的序列劃分左右兩個左閉右開的區間[l,m) [m, r),然後遞迴求出左邊的區間的最大連續和,再求出右邊的最大連續和,最後在求出跨中點m的最大連續和,這個區間的最大連續和就在這三者之中。將每個區間都劃分成子問區間,直到區間只有乙個數的時候停止。注意:兩個區間不能同時擁有同乙個元素,這樣在後面的區間再劃分的時候會出錯(劃分成單個元素區間的時候會發現每個元素都多了乙個),這也是要左閉右開區間的原因。偽**:
l = 1;
r = len + 1;
function maxsum (l, r)
if r - l == 1: return s[l];
m = (l + r) / 2;
//左子區間
maxlp = maxsum(l, m);
//右子區間
maxrp = maxsum(m, r);
max = max(maxlp, maxrp);
//中間的跨越中點m的區間
sumlp = s[m - 1];
sum = 0;
for i in range(m - 1, l)://i--
sum += s[i];
maxlp = max(maxlp, sum);
else:;
sumrp = s[m];
sum = 0;
for i in range(m, r - 1)://i++
sum += s[i];
maxrp = max(maxrp, sum);
else:;
return max(max, maxrp + maxlp);
end function
第四種:累積遍歷演算法(1),複雜度o(n).從左到右用乙個sum變數累計,當sum<0的時候就令sum = 0,再去加上後面的數,並且在這個遍歷的過程中記錄下sum的最大值。換句話說:用這個sum將這個序列分段,如果前面的連續和比0還小的話,那麼加上後面的數倒還沒有後面的數自己作為開頭的連續和大,所以就讓sum = 0.這樣才可能產生比前面的連續和更大的值。偽**:
s[maxn];
max = sum = s[1];
for i in range(2, len):
if sum < 0:
sum = s[i];
else:
sum += s[i];
max = max (max, sum);
else: return max;
第五種:累積遍歷演算法(2),複雜度o(n).同樣也是用乙個變數sum來累計,只是這裡用乙個minpart來儲存前面從1開始的最小的連續和。因為這個序列的所有值的和是確定的,那麼只要取得了前面的最小連續和,因為總值是固定的,後面的就是最大的連續和,也就是遍歷過程中sum - minpart的最大值。偽**;
s[maxn];
minpart = sum = 0;//注意開頭可能是正數
max = s[1];
for in range(1, len):
sum += s[i];
max = max (sum - minpart);
minpart = min (minpart, sum);
else: return max;
第六種:動態規劃,複雜度o(n)。和第四種的想法是類似的,只是這裡是開乙個sum[maxn]的陣列。狀態轉移方程:sum[i] = max(sum[i - 1] + s[i], s[i]);偽**:
sum[maxn],s[maxn];
max = sum[1] = s[1];//只有乙個數的情況下只能取這個數
for in range(2, len):
sum[i] = max (sum[i - 1] + s[i], s[i])
max = max (max, sum[i]);
else: return max;
接下來是uva507的題目的四種解法,因為前兩種比較簡單而且放在這題會超時就不寫了。這題需要給出上下界的。
分治法
#include
#include
#include
using
namespace
std;
const
int maxn = 2e4 + 5;
int n, s[maxn];
struct ans
ans (int v, int l , int r) : v(v), l(l), r(r){}
void
set (int v, int l, int r)
};ans maxsum (int l, int r)
ans lp(s[m - 1], m - 1, m - 1), rp(s[m], m, m);
int tmp = 0;
for (int i = m - 1; i >= l; i--)
tmp = 0;
for (int i = m; i < r; i++)
ans center(lp.v + rp.v, lp.l, rp.r);
if (center.v > max_sum.v)
max_sum = center;
else
if (center.v == max_sum.v)
return max_sum;
}int main ()
return
0;
}
累計遍曆法(1):#include
#include
#include
using
namespace
std;
const
int maxn = 2e4 + 5;
int n, s[maxn];
int solve (int& l, int& r) else
sum += s[i];
if (sum > max_num) else
if (sum == max_num) }}
return max_num;
}int main ()
return
0;
}
累積遍曆法(2):#include
#include
#include
using
namespace
std;
const
int maxn = 2e4 + 5;
int n, s[maxn];
int solve (int& l, int& r) else
if (sum - min_sum == max_sum) }}
if (sum < min_sum)
}return max_sum;
}int main ()
return
0;
}
動態規劃:#include
#include
#include
using
namespace
std;
const
int maxn = 2e4 + 5;
int n, s[maxn], sum[maxn];
int solve (int& l, int& r) else
sum[i] = sum[i - 1] + s[i];
//max
if (max_sum <= sum[i]) else
if (max_sum == sum[i] && (i + 1 - tmp > r - l)) }}
return max_sum;
}int main ()
return
0;
}
感謝gg大神:參考部落格 最大連續和
這個問題對我來說還挺難的,當初做dp時水過去了,但沒徹底理解,這次打算好好分析一下,爭取徹底搞懂。首先,像 1 1 2 2 3 3 4 4 5 5這樣的數列,想要找連續最大和,可以有很多種方法,從最慢的列舉o n 3 到最快的動態規劃o n 毫無疑問,我們要選擇複雜度低的演算法。所以我這裡就只分析兩...
最大連續和
求陣列中數的最大連續和,如 1,1,1,1,1 最大連續和為3 一 動態規劃 當我們從頭到尾遍歷這個陣列的時候,對於陣列裡的乙個整數,它有幾種選擇呢?它只有兩種選擇 1 加入之前的subarray 2.自己另起乙個subarray。那什麼時候會出現這兩種情況呢?設狀態為f j 表示以s j 結尾的最...
最大連續和
給出乙個長度為n的序列a1,a2,an,求最大連續和 使用列舉 時間複雜度o n 3 best a 1 初始最大值 for int i 1 i n i 設si a1 a2 ai,則ai ai 1 aj sj si 1 連續子串行的和等於兩個字首之差 時間複雜度o n 2 s 0 0 for int ...