luogu 1169 棋盤制作(單調棧/懸線)
luogu 1169 棋盤制作(單調棧/懸線)
國際象棋是世界上最古老的博弈遊戲之一,和中國的圍棋、象棋以及日本的將棋同享盛名。據說國際象棋起源於易經的思想,棋盤是一個8*8大小的黑白相間的方陣,對應八八六十四卦,黑白對應陰陽。而我們的主人公小Q,正是國際象棋的狂熱愛好者。作為一個頂尖高手,他已不滿足於普通的棋盤與規則,於是他跟他的好朋友小W決定將棋盤擴大以適應他們的新規則。小Q找到了一張由N*M個正方形的格子組成的矩形紙片,每個格子被塗有黑白兩種顏色之一。小Q想在這種紙中裁減一部分作為新棋盤,當然,他希望這個棋盤盡可能的大。不過小Q還沒有決定是找一個正方形的棋盤還是一個矩形的棋盤(當然,不管哪種,棋盤必須都黑白相間,即相鄰的格子不同色),所以他希望可以找到最大的正方形棋盤面積和最大的矩形棋盤面積,從而決定哪個更好一些。於是小Q找到了即將參加全國信息學競賽的你,你能幫助他麽?N, M ≤ 2000。
首先,一個是棋盤的矩陣,滿足相鄰兩個元素顏色不同。假設左上角的(1,1)是白色的格子。那麽易證,橫縱坐標之和是偶數的格子是白色的,是奇數的各自是黑色的。所以逆向思維,只要把給定的矩陣,橫縱坐標之和同模2的一種格子反色,問題就變成了求最大的同色矩陣和同色正方形。這是最大子矩陣問題。用單調棧可以做,不過比懸線法煩一些。下面我來介紹一下懸線法。
懸線就是一端貼著不可算入矩形的區域(墻壁)的線。首先,一個最大子矩陣中一定有懸線,不然這個矩陣是可以再擴張的。所以,我們只要枚舉所有懸線即可。首先n^2預處理出每個點向上向下能擴展的最大距離。然後從左向右,從右向左分別枚舉一遍所有懸線(橫向的),找出最大值即可。詳見代碼。
還有,個人感覺這種題目的思路很重要,就是把求最優解,轉化為求出每個元素作為基礎的最優值,然後取最優值中的最優值作為答案。
#include <cstdio>
using namespace std;
const int maxn=2005, INF=1e9;typedef int inta2[maxn][maxn];
int n, m, ans1, ans2, begin, minup, mindown;
inta2 a, up, down;
int max(int x, int y){ return x<y?y:x; }
int min(int x, int y){ return x<y?x:y; }
int sqr(int x){ return x*x; }
int main(){
scanf("%d%d", &n, &m);
for (int i=1; i<=n; ++i) //轉換棋盤
for (int j=1; j<=m; ++j){
scanf("%d", &a[i][j]);
if (i&1) a[i][j]^=1;
if (!(j&1)) a[i][j]^=1;
}
for (int i=1; i<=n; ++i)
for (int j=1; j<=m; ++j)
up[i][j]=(a[i][j]==a[i-1][j]?
up[i-1][j]+1:1);
for (int i=n; i>0; --i)
for (int j=1; j<=m; ++j)
down[i][j]=(a[i][j]==a[i+1][j]?
down[i+1][j]+1:1);
//懸線法
for (int color=0; color<=1; ++color)
for (int i=1; i<=n; ++i){
minup=mindown=INF; begin=1;
for (int j=1; j<=m; ++j){
if (a[i][j]!=color){
minup=mindown=INF;
begin=j+1; continue;
}
minup=min(minup, up[i][j]);
mindown=min(mindown, down[i][j]);
ans1=max(ans1, (minup+mindown-1)*(j-begin+1));
ans2=max(ans2, sqr(min(minup+mindown-1, j-begin+1)));
}
minup=mindown=INF; begin=m;
for (int j=m; j>0; --j){
if (a[i][j]!=color){
minup=mindown=INF;
begin=j-1; continue;
}
minup=min(minup, up[i][j]);
mindown=min(mindown, down[i][j]);
ans1=max(ans1, (minup+mindown-1)*(begin-j+1));
ans2=max(ans2, sqr(min(minup+mindown-1, begin-j+1)));
}
}
printf("%d\n%d", ans2, ans1);
return 0;
}
luogu 1169 棋盤制作(單調棧/懸線)