BZOJ1057:[ZJOI2007]棋盘制作——题解
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/+
+++++++++++++++++++++++++++++++++++++++++++