1. 程式人生 > >luogu 1169 棋盤制作(單調棧/懸線)

luogu 1169 棋盤制作(單調棧/懸線)

nta 面積 顏色 之一 cnblogs include 奇數 中國 blog

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 棋盤制作(單調棧/懸線)