P1169 [ZJOI2007]棋盤製做 && 懸線法

P1169 [ZJOI2007]棋盤製做

給出一個 \(N * M\)\(01\) 矩陣, 求最大的正方形和最大的矩形交錯子矩陣
\(n , m \leq 2000\)ios

懸線法

懸線法能夠求出給定矩陣中知足條件的最大子矩陣spa

對於每一個點, 維護 兩條等長的線段, 兩線段的底部達到此點的縱座標, 分別表明能從這個點達到的最左 / 最右端點
大概長這樣code

l        r
   |        |
   |        |
   |        |
   |        |
   |    *   |

那麼枚舉每一個點的這兩條線段, 不斷用 \((r - l + 1) * dis\) 更新答案便可
這就是懸線法繼承

這兩條線段看上去很難維護, 其實否則
由於其等長, 咱們將這兩條線段用以下幾個屬性表示:
\(l[i][j]\) 表示從 \((i, j)\) 能達到的最左的座標
\(r[i][j]\) 表示從 \((i, j)\) 能達到的最右的座標
\(up[i][j]\) 表示 \((i, j)\) 向上達到的 最上座標, 即懸線的長度get

初始化知足條件的每一個\(1 * 1\) 小矩陣 \(l[i][j] = r[i][j] = j, up[i][j] = 1\), 即圍成一個 \(1 * 1\) 的小小矩形string

容易想到維護懸線能夠遞推, 在知足矩陣限制的條件下, 先初始化
\[l[i][j] = l[i][j - 1]\] \[r[i][j] = r[i][j + 1]\]it

比對上一行,在知足矩陣限制的條件下, 咱們只能取最窄知足條件
\[l[i][j] = max(l[i][j], l[i - 1][j])\] \[r[i][j] = min(r[i][j], r[i - 1][j])\]
而後懸線長度能夠繼承上一行的 \[up[i][j] = up[i - 1][j] + 1\]io

有了懸線直接計算圍出來的面積便可class

Solution

此題求最大交錯矩陣
交錯矩陣任意相鄰兩格顏色不一樣
因而限制條件即爲相鄰兩格顏色不等
放個代碼理解 懸線法stream

Code

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
#include<climits>
#define LL long long
#define REP(i, x, y) for(int i = (x);i <= (y);i++)
using namespace std;
int RD(){
    int out = 0,flag = 1;char c = getchar();
    while(c < '0' || c >'9'){if(c == '-')flag = -1;c = getchar();}
    while(c >= '0' && c <= '9'){out = out * 10 + c - '0';c = getchar();}
    return flag * out;
    }
const int maxn = 2019;
int lenx, leny;
int map[maxn][maxn];
int l[maxn][maxn], r[maxn][maxn];
int up[maxn][maxn];
int ans1, ans2;
void init(){
    lenx = RD(), leny = RD();
    REP(i, 1, lenx)REP(j, 1, leny){
        map[i][j] = RD();
        l[i][j] = r[i][j] = j;
        up[i][j] = 1;
        }
    REP(i, 1, lenx)REP(j, 2, leny){
        if(map[i][j] != map[i][j - 1])l[i][j] = l[i][j - 1];//預處理左邊界
        }
    REP(i, 1, lenx)for(int j = leny - 1;j >= 1;j--){
        if(map[i][j] != map[i][j + 1])r[i][j] = r[i][j + 1];//右邊界
        }
    }
void solve(){
    REP(i, 1, lenx)REP(j, 1, leny){
        if(i > 1 && map[i][j] != map[i - 1][j]){
            l[i][j] = max(l[i][j], l[i - 1][j]);
            r[i][j] = min(r[i][j], r[i - 1][j]);
            up[i][j] = up[i - 1][j] + 1;
            }
        int a = r[i][j] - l[i][j] + 1;//寬
        int b = min(a, up[i][j]);
        ans1 = max(ans1, b * b);
        ans2 = max(ans2, a * up[i][j]);
        }
    printf("%d\n%d\n", ans1, ans2);
    }
int main(){
    init();
    solve();
    return 0;
    }
相關文章
相關標籤/搜索