1. 程式人生 > >BZOJ1057:[ZJOI2007]棋盤制作——題解

BZOJ1057:[ZJOI2007]棋盤制作——題解

學習 ++ 世界 HP you href tdi algo 單調棧

http://www.lydsy.com/JudgeOnline/problem.php?id=1057

https://www.luogu.org/problemnew/show/P1169

國際象棋是世界上最古老的博弈遊戲之一,和中國的圍棋、象棋以及日本的將棋同享盛名。據說國際象棋起源於易經的思想,棋盤是一個8*8大小的黑白相間的方陣,對應八八六十四卦,黑白對應陰陽。

而我們的主人公小Q,正是國際象棋的狂熱愛好者。作為一個頂尖高手,他已不滿足於普通的棋盤與規則,於是他跟他的好朋友小W決定將棋盤擴大以適應他們的新規則。

小Q找到了一張由N*M個正方形的格子組成的矩形紙片,每個格子被塗有黑白兩種顏色之一。小Q想在這種紙中裁減一部分作為新棋盤,當然,他希望這個棋盤盡可能的大。

不過小Q還沒有決定是找一個正方形的棋盤還是一個矩形的棋盤(當然,不管哪種,棋盤必須都黑白相間,即相鄰的格子不同色),所以他希望可以找到最大的正方形棋盤面積和最大的矩形棋盤面積,從而決定哪個更好一些。

於是小Q找到了即將參加全國信息學競賽的你,你能幫助他麽?

參考了洛谷題解以及大量講最大子矩陣的問題。

今天學了一天的這玩意就是為了解這道題的。

接論文的算法1和算法2,我們還可以單調棧維護dp做。

首先先把圖形轉換一下,引用洛谷題解。

對於圖上所有的棋盤一定屬於以下兩種類型:

1.黑格行列奇偶性相同,白格不同

2.白格行列奇偶性相同,黑格不同

那麽第一種情況的格子設為0,第二種為1。

則這道題轉換成了求0或1的矩陣/正方形最大面積。

我們可以使用懸線法,這裏使用單調棧為以後的學習打個基礎。

我們預處理出每個格子最多可以往上延伸多少格子,單調棧維護一下,同時記錄它的最遠左邊界。

顯然如果棧沒有彈出那麽說明這個格子是和前面棧裏的元素是割裂開的,於是正常處理。

否則不斷彈出棧頂元素並且更新,同時更新我們即將加入的元素的最遠左邊界(就是最晚彈出的那個元素的最遠左邊界啦)

#include<cstdio>
#include<queue>
#include<cctype>
#include<cstring>
#include
<vector> #include<algorithm> using namespace std; const int N=2010; inline int read(){int x;scanf("%d",&x);return x;} int n,m,ans1,ans2,mp[N][N],nw[N][N]; int q[N][2]; void init(int k){ for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ if(mp[i][j]==k)nw[i][j]=nw[i-1][j]+1; else nw[i][j]=0; } } } void suan(int x){ int t,l=0; for(int i=1;i<=m+1;i++){ t=i; while(q[l][0]>=nw[x][i]&&l){ int len=min(i-q[l][1],q[l][0]); ans1=max(ans1,len*len); ans2=max(ans2,(i-q[l][1])*q[l][0]); t=q[l--][1]; } q[++l][0]=nw[x][i];q[l][1]=t; } } int main(){ n=read(),m=read(); for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ int a=read(); if(((i&1)==(j&1)&&a)||((i&1)!=(j&1)&&!a))mp[i][j]=1; } } for(int i=0;i<=1;i++){ init(i); for(int j=1;j<=n;j++)suan(j); } printf("%d\n%d\n",ans1,ans2); return 0; }

+++++++++++++++++++++++++++++++++++++++++++

+本文作者:luyouqi233。               +

+歡迎訪問我的博客:http://www.cnblogs.com/luyouqi233/+

+++++++++++++++++++++++++++++++++++++++++++

BZOJ1057:[ZJOI2007]棋盤制作——題解