最大欄位和-模板
阿新 • • 發佈:2021-11-08
一般
就是給定一個數組讓求一段和最大
dp[i]表示加上第i個數後,前i個數最大和
int a[] = {0,-2,5,6,-7,5,-13,7,8,-5};
dp[0] = 0;
for(int i = 1;i <= len;i ++){
if(dp[i-1] < 0) dp[i] = a[i];
else dp[i] = dp[i-1]+a[i];
}
int ans = 0;
for(int i = 1;i <= len;i++) ans = max(ans,dp[i])
環形陣列
環形陣列求最大和的話一般做法就是在原本陣列下面再複製一組--長度由n->2n
需要注意的就是防止陣列重複累加
int a[] = {-2,5,6,-7,5,-13,7,8,-5};
for(int i = 0;i < 2;i++) // 複製一組
for(int j = 1;j <= len;i++)
b[i*len+j] = a[j];
// 求
int ans = -1000000;
for(int i = 1;i <= 2*len && i < sta+len;i++){
if(dp[i-1] < 0){
pre = i; // 記錄每一段的起點
dp[i] = b[i];
}else dp[i] = dp[i-1]+b[i];
if(dp[i] > ans){
ans = dp[i];
sta = pre; // 更新更大的一個段
end = i;
}
}
上面一種我提交時有兩個測試樣例過不了
還有一種解決這種環形陣列的方法
你會發現最終取得的結果分為兩種
-
沒有實現前後相連---正常求解
-
實現了前後相連的情況---中間一部分(欄位和最小)沒有取
即ans = max(最大值,sum(1,len)-sum(i,j));
子矩陣段求和
給你一個矩陣,求子矩陣的最大和
這看起來確實挺難的,但仔細想想
-
先分析一行的矩陣,就是線性子段求和
-
兩行的矩陣,結果就是要麼就是一行裡的欄位,要麼就是兩行的欄位
-
一行線性求解
-
兩行的話將兩行對應列求和,就變成線性求解了
-
-
依次類推,n行的話
-
就有
-
int a[][] = { // n*m矩陣
{-2,5,6,-7,5,-13,7,8,5},
{2,-5,-6,7,-5,13,-7,-8,-5},
{-2,5,6,-7,5,-13,7,8,5},
{2,-5,-6,7,-5,13,-7,-8,-5},
{-2,5,6,-7,5,-13,7,8,5}
};
// 先求每列的字首和
for(int i = 1;i <= m;i++)
for(int j = n;j > 0;j--)
sum_col[j][i] = sum_col[j+1][i] + a[j][i];
for(int i = 0;i < n;i++)
for(int j = i+1;j <= n;j++){
for(int k = 1;k <= m;k++){
b[k] = sum_col[j][k] - sum_col[i][k];
}
ans = max(ans,sumMax(b));
}
求子段和大於0且最小
在51nod上看到了
一開始確實看懵了,資料n最大可取50000,所以暴力n^2會超時
有一種O(n)的解法
-
求出每一項字首和並記錄對應的位置
-
以字首和進行升序排序
-
然後就可以進行判斷
-
如果說後一項 i(字首和肯定>=前一項)大於前一項 i-1 ,且後一項 i 的位置在前一項 i 的後面的話 (假設i對應位置為 m ,i - 1 對應的位置為 n )
-
這樣就有一段連續空間 m - n 可能符合條件
-
最終找出所有可能符合條件的最小值
-
struct Node{
int pos;
ll val;
}nd[N];
a[i]表示前i個數字首和
bool cmp(Node n1,Node n2) {
return n1.val < n2.val;
}
for(int i = 1;i <= n;i++) nd[i].pos = i,nd[i].val = a[i];
sort(nd,nd+1+n,cmp);
ll ans = 0x7fffffff;
for(int i = 1;i <= n;i++){
if(nd[i].val > nd[i-1].val && nd[i].pos > nd[i-1].pos)
ans = min(ans,nd[i].val-nd[i-1].val);
}