1. 程式人生 > >Luogu1681_ 最大正方形II

Luogu1681_ 最大正方形II

題目背景

忙完了學校的事,v神終於可以做他的“正事”:陪女朋友散步。一天,他和女朋友走著走著,不知不覺就來到了一個千里無煙的地方。v神正要往回走,如發現了一塊牌子,牌子上有有一行小字和一張圖,小字說道:“找到圖上最大的交錯正方形之後和我聯絡,這塊地就是你的了。”在房價瘋長的年代,v神當然不願錯過這個機會,於是開始找了起來……以v神的能力當然找不出來了,你能幫v神找出來嗎?

題目描述

圖上有一個矩陣,由N*M個格子組成,這些格子由兩種顏色構成,黑色和白色。請找到面積最大的且內部是黑白交錯(即兩個相連的正方形顏色不能相同)的正方形。

輸入格式:

第一行兩個整數N和M,分別表示行數和列數。接下來有N行,每行M個數,0或1分別表示這個格子是黑色或白色。

輸出格式:

僅有一行,表示滿足條件最大正方形的 邊長

樣例

INPUT

3 3
0 1 0
1 0 0
1 1 1

OUTPUT

2

HINT

樣例解釋:

(1,1)到(2,2)這個正方形是滿足條件的,它的邊長是2

資料範圍約定:

對於30%的資料,\(N\leq20\)

對於60%的資料,\(N\leq300\)

對於100%的資料,\(N\leq1500\)

SOLUTION

題解:dp
一看資料範圍就知道是\(O(n^2)\)的演算法,再說了這題如果要暴力的話最暴力的可以達到\(O(n^6)\)之高。(有沒有其他暴力我不知道qwq)

所以考慮通過題目的性質進行優化。

根據題意,正方形的合法與否在於相鄰格子之間關係的合法與否。所以我們可以先處理左右之間關係,在處理上下之間的關係。這裡就可以用\(lft[i][j],rgt[i][j]\)

陣列在同一層掃一遍時維護一下第\(i\)\(j\)個格子最左/右可以延伸到哪一格,這裡注意一下維護順序,\(lft[][]\)陣列是從左到右,而\(rgt[][]\)是從右到左。

然後處理上下層之間的關係就可以直接進行層與層之間的轉移了
\[lft[i][j]=max(lft[i][j],lft[i-1][j])\]
\[rgt[i][j]=min(rgt[i][j],rgt[i-1][j])\]
\[hgt[i][j]=hgt[i-1][j]+1\]

這個\(hgt\)指的是高度(縱向長度)

然後維護過程中順帶記錄一下最大值就可以了

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
using namespace std;
const int N=1510;
int n,m,hgt[N][N],sq[N][N],lft[N][N],rgt[N][N];
int main(){
    int i,j;
    scanf("%d%d",&n,&m);
    for (i=1;i<=n;++i)
        for (j=1;j<=m;++j){
            scanf("%d",&sq[i][j]);
            lft[i][j]=j;rgt[i][j]=j;hgt[i][j]=1;
        }
    for (i=1;i<=n;++i)
        for (j=2;j<=m;++j)
            if (sq[i][j]^sq[i][j-1]) lft[i][j]=lft[i][j-1];
    for (i=1;i<=n;++i)
        for (j=m-1;j>=1;--j)
            if (sq[i][j]^sq[i][j+1]) rgt[i][j]=rgt[i][j+1]; 
    int ans=0;
    for (i=1;i<=n;++i)
        for (j=2;j<=m;++j){
            if (i>1&&(sq[i][j]^sq[i-1][j])){
                lft[i][j]=max(lft[i][j],lft[i-1][j]);
                rgt[i][j]=min(rgt[i][j],rgt[i-1][j]);
                hgt[i][j]=hgt[i-1][j]+1;
            }
            int a=min(rgt[i][j]-lft[i][j]+1,hgt[i][j]);
            ans=max(ans,a);
        }
    printf("%d\n",ans);
    return 0;
}