标签:zjoi2007 爱好者 strong str 悬线法 amp name its 个数
国际象棋是世界上最古老的博弈游戏之一,和中国的围棋、象棋以及日本的将棋同享盛名。据说国际象棋起源于易经的思想,棋盘是一个8×8大小的黑白相间的方阵,对应八八六十四卦,黑白对应阴阳。
而我们的主人公小Q
,正是国际象棋的狂热爱好者。作为一个顶尖高手,他已不满足于普通的棋盘与规则,于是他跟他的好朋友小W
决定将棋盘扩大以适应他们的新规则。
小Q
找到了一张由N×M个正方形的格子组成的矩形纸片,每个格子被涂有黑白两种颜色之一。小Q
想在这种纸中裁减一部分作为新棋盘,当然,他希望这个棋盘尽可能的大。
不过小Q
还没有决定是找一个正方形的棋盘还是一个矩形的棋盘(当然,不管哪种,棋盘必须都黑白相间,即相邻的格子不同色),所以他希望可以找到最大的正方形棋盘面积和最大的矩形棋盘面积,从而决定哪个更好一些。
于是小Q
找到了即将参加全国信息学竞赛的你,你能帮助他么?
包含两个整数N和M,分别表示矩形纸片的长和宽。接下来的NNN行包含一个N ×M的01矩阵,表示这张矩形纸片的颜色(0表示白色,1表示黑色)。
输出格式:包含两行,每行包含一个整数。第一行为可以找到的最大正方形棋盘的面积,第二行为可以找到的最大矩形棋盘的面积(注意正方形和矩形是可以相交或者包含的)。
3 3
1 0 1
0 1 0
1 0 0
4
6
对于20%的数据,N,M≤80
对于40%的数据,N,M≤400
对于100%的数据,N,M≤2000
Solution:
本题dp。
第一问是常规dp,第二问奇偶分出黑白点,再分别对黑点取反求下最大的全0或全1矩形,然后对全图取反再求一次取max就好了。
这里主要安利一个很骚的做法,据说叫做悬线法。
思路很简单,开三个数组:$lf[i][j]$表示$(i,j)$点往左最远的合法位置,同理$rf[i][j]$表示往右的最远合法位置、$up[i][j]$表示往上的最远合法位置,这些直接$n^2$预处理就好了。
然后就是枚举每个位置,求出合法的长和宽,更新答案就好了(实现时的细节就是把$up[i][j]$在枚举位置的时候求,并把其含义改为合法宽度,对于每个宽度都应该求下合法面积)。
代码:
/*Code by 520 -- 9.20*/ #include<bits/stdc++.h> #define il inline #define ll long long #define RE register #define For(i,a,b) for(RE int (i)=(a);(i)<=(b);(i)++) #define Bor(i,a,b) for(RE int (i)=(b);(i)>=(a);(i)--) using namespace std; const int N=2005; int n,m,mp[N][N],lf[N][N],rf[N][N],up[N][N]; int ans1,ans2; int main(){ scanf("%d%d",&n,&m); For(i,1,n) For(j,1,m) scanf("%d",&mp[i][j]),lf[i][j]=rf[i][j]=j,up[i][j]=1; For(i,1,n) For(j,2,m) if(mp[i][j]^mp[i][j-1]) lf[i][j]=lf[i][j-1]; For(i,1,n) Bor(j,1,m-1) if(mp[i][j]^mp[i][j+1]) rf[i][j]=rf[i][j+1]; For(i,1,n) For(j,1,m) { if(i>1&&(mp[i][j]^mp[i-1][j])){ lf[i][j]=max(lf[i][j],lf[i-1][j]); rf[i][j]=min(rf[i][j],rf[i-1][j]); up[i][j]=up[i-1][j]+1; } int c=rf[i][j]-lf[i][j]+1; int r=min(c,up[i][j]); ans1=max(ans1,r*r),ans2=max(ans2,c*up[i][j]); } cout<<ans1<<‘\n‘<<ans2; return 0; }
标签:zjoi2007 爱好者 strong str 悬线法 amp name its 个数
原文地址:https://www.cnblogs.com/five20/p/9688811.html