【洛谷】P1169棋盘制作(悬线法)

题目链接

题目描述

国际象棋是世界上最古老的博弈游戏之一,和中国的围棋、象棋以及日本的将棋同享盛名。据说国际象棋起源于易经的思想,棋盘是一个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)时间内求解满足条件的最大子矩阵,效率比一般的枚举法高得多。

详情可参考:

ACM算法:悬线法

浅谈用极大化思想解决最大子矩阵问题

合法的棋盘可看做是一条悬线(竖着的)左右平移得来的,因此只要求出悬线的高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  
View Code
原文地址:https://www.cnblogs.com/wangzhebufangqi/p/12796132.html