1. 程式人生 > 實用技巧 >4260. 最大子矩陣 (Standard IO)

4260. 最大子矩陣 (Standard IO)

洛谷看題通道

首先,我們來一波推論。

假設上圖中 $ABCD$ 和 $EDEF$均滿足題目條件,那麼,

$f_A + f_D \le f_C + f_B$

$f_C + f_F \le f_D + f_E$

兩柿子相加,自然得到: $f_A + f_F \le f_B + f_E$

所以兩個合法的矩陣可以合成一個大的合法矩陣

接著,考慮如何求出最大的矩陣:

先求出所有的合法矩陣,統計每一行中列合法的長度的分段字首和。

接著,我們用單調棧,儲存每個合法矩陣區間的縱座標。為了保證可以拼湊出矩陣,所以遇到長度更短的,立即將前面的矩陣區間進行結算。 當然,最後不要忘了最右邊的剩餘未結算矩陣,放在迴圈外處理。

來個栗子:

對於波峰 $1,2$,遇到更短的$3$,令它們單成一個合法矩陣區間,用矩陣的面積公式結算。最後,我們會留下一個最短的矩陣區間,單獨計算。

#include <bits/stdc++.h>
using namespace std;
#define N 1010

inline int read(){
    int x = 0, s = 1;
    char c = getchar();
    while(!isdigit(c)){
        if(c == '-') s = -1;
        c = getchar();
    }
    
while(isdigit(c)){ x = x * 10 + (c ^ '0'); c = getchar(); } return x * s; } int stac[N], top = 0; // 存座標 int ans = -666; int a[N][N], b[N][N]; int main(){ int n = read(), m = read(); for(int i = 1; i <= n; i++) for(int j = 1;j <= m; j++) a[i][j]
= read(), b[i][j] = 1; for(int i = 1;i <= n; i++) for(int j = 1;j <= m; j++) if(a[i][j] + a[i-1][j-1] <= a[i-1][j] + a[i][j-1]) b[i][j] = b[i-1][j] + 1; for(int i = 1;i <= n; i++) for(int j = 1; j <= m; j++) if(b[i][j] == 1) b[i][j] = 0; stac[0] = 1; for(int i = 2;i <= n; in++){ top = 0; for(int j = 2;j <= m; j++){ while(top && b[i][j] <= b[i][stac[top]]){ int temp = b[i][stac[top]] * (j - stac[--top]); ans = max(ans, temp); } stac[++top] = j; } while(top){ ans = max(ans, b[i][stac[top]] * (m - stac[--top] + 1)); } } printf("%d\n", ans); return 0; }