题目描述
国际象棋是世界上最古老的博弈游戏之一,和中国的围棋、象棋以及日本的将棋同享盛名。据说国际象棋起源于易经的思想,棋盘是一个8×8大小的黑白相间的方阵,对应八八六十四卦,黑白对应阴阳。
而我们的主人公小Q
,正是国际象棋的狂热爱好者。作为一个顶尖高手,他已不满足于普通的棋盘与规则,于是他跟他的好朋友小W
决定将棋盘扩大以适应他们的新规则。
小Q
找到了一张由N×M个正方形的格子组成的矩形纸片,每个格子被涂有黑白两种颜色之一。小Q
想在这种纸中裁减一部分作为新棋盘,当然,他希望这个棋盘尽可能的大。
不过小Q
还没有决定是找一个正方形的棋盘还是一个矩形的棋盘(当然,不管哪种,棋盘必须都黑白相间,即相邻的格子不同色),所以他希望可以找到最大的正方形棋盘面积和最大的矩形棋盘面积,从而决定哪个更好一些。
于是小Q
找到了即将参加全国信息学竞赛的你,你能帮助他么?
输入输出格式
输入格式:
包含两个整数N和M,分别表示矩形纸片的长和宽。接下来的NN行包含一个N ×M的01矩阵,表示这张矩形纸片的颜色(0表示白色,1表示黑色)。
输出格式:
包含两行,每行包含一个整数。第一行为可以找到的最大正方形棋盘的面积,第二行为可以找到的最大矩形棋盘的面积(注意正方形和矩形是可以相交或者包含的)。
输入输出样例
输入样例#1: 复制
3 3 1 0 1 0 1 0 1 0 0
输出样例#1: 复制
4 6
说明
对于20%的数据,N, M ≤ 80N,M≤80
对于40%的数据,N, M ≤ 400N,M≤400
对于100%的数据,N, M ≤ 2000N,M≤2000
悬线法可在O(m*n)时间内求解满足条件的最大子矩阵,效率比一般的枚举法高得多。
详情可参考:
合法的棋盘可看做是一条悬线(竖着的)左右平移得来的,因此只要求出悬线的高H,悬线能平移到最左的位置L,悬线能平移到最右的位置R,就能算出面积H*(L-R+1),矩形棋盘在里面求最大值即可,正方形棋盘先求出边长s=min(L-R+1,H),在s*s里面取最大值即可。
设H[i][j]为底部端点是(i,j)的悬线的高度,L[i][j]为底部端点是(i,j)的悬线能平移到最左的位置,R[i][j]为底部端点是(i,j)的悬线能平移到最右的位置。
(图片来源于https://blog.csdn.net/clover_hxy/article/details/50532289?locationNum=1&fps=1)
可知递推式为
R[i][j]=min(R[i][j],R[i-1][j])
L[i][j]=max(L[i][j],L[i-1][j])
AC代码:
1 #include<iostream> 2 #include<sstream> 3 #include<algorithm> 4 #include<string> 5 #include<cstring> 6 #include<iomanip> 7 #include<vector> 8 #include<cmath> 9 #include<ctime> 10 #include<stack> 11 #include<queue> 12 #define e 2.71828182 13 #define Pi 3.141592654 14 using namespace std; 15 int N,M,cb[2018][2018],H[2018][2018],L[2018][2018],R[2018][2018]; 16 int main() 17 { 18 cin>>N>>M; 19 for(int i=1;i<=N;i++) 20 for(int j=1;j<=M;j++) 21 cin>>cb[i][j]; 22 //这部分求的是以(i,j)为底部端点的悬线的长度 23 for(int j=1;j<=M;j++) H[1][j]=1; 24 for(int i=2;i<=N;i++) 25 for(int j=1;j<=M;j++) 26 if(cb[i][j]^cb[i-1][j]) H[i][j]=H[i-1][j]+1;//cb[i][j]与cb[i-1][j]棋子一黑一白 27 else H[i][j]=1; 28 //预处理,这部分求的是(i,j)能平移到最左的位置,并非是以(i,j)为底部端点的悬线能平移到最左的位置 29 for(int i=1;i<=N;i++) L[i][1]=1; 30 for(int i=1;i<=N;i++) 31 for(int j=2;j<=M;j++) 32 if(cb[i][j]^cb[i][j-1]) L[i][j]=L[i][j-1];//cb[i][j]与cb[i][j-1]棋子一黑一白 33 else L[i][j]=j; 34 //预处理,这部分求的是(i,j)能平移到最右的位置,并非是以(i,j)为底部端点的悬线能平移到最右的位置 35 for(int i=1;i<=N;i++) R[i][M]=M; 36 for(int i=1;i<=N;i++) 37 for(int j=M-1;j>=1;j--) 38 if(cb[i][j]^cb[i][j+1]) R[i][j]=R[i][j+1];//cb[i][j]与cb[i][j+1]棋子一黑一白 39 else R[i][j]=j; 40 41 int ans1=0,ans2=0;//最大正方形棋盘,最大矩形棋盘 42 for(int i=1;i<=N;i++) 43 { 44 for(int j=1;j<=M;j++) 45 { 46 if(i>1&&(cb[i][j]^cb[i-1][j]))//递推 47 R[i][j]=min(R[i][j],R[i-1][j]),L[i][j]=max(L[i][j],L[i-1][j]); 48 int l=R[i][j]-L[i][j]+1; 49 int w=min(l,H[i][j]);//正方形的边长取l和H的较小值 50 ans1=max(ans1,w*w); 51 ans2=max(ans2,l*H[i][j]); 52 } 53 } 54 55 cout<<ans1<<endl<<ans2; 56 } 57