1. 程式人生 > 其它 >最大欄位和-模板

最大欄位和-模板

題型

一般

就是給定一個數組讓求一段和最大

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);
}