ZJOI2007 棋盘制作
【题目描述】
国际象棋是世界上最古老的博弈游戏之一,和中国的围棋、象棋以及日本的将棋同享盛名。据说国际象棋起源于易经的思想,棋盘是一个8*8大小的黑白相间的方阵,对应八八六十四卦,黑白对应阴阳。而我们的主人公小Q,正是国际象棋的狂热爱好者。作为一个顶尖高手,他已不满足于普通的棋盘与规则,于是他跟他的好朋友小W决定将棋盘扩大以适应他们的新规则。小Q找到了一张由N*M个正方形的格子组成的矩形纸片,每个格子被涂有黑白两种颜色之一。小Q想在这种纸中裁减一部分作为新棋盘,当然,他希望这个棋盘尽可能的大。不过小Q还没有决定是找一个正方形的棋盘还是一个矩形的棋盘(当然,不管哪种,棋盘必须都黑白相间,即相邻的格子不同色),所以他希望可以找到最大的正方形棋盘面积和最大的矩形棋盘面积,从而决定哪个更好一些。于是小Q找到了即将参加全国信息学竞赛的你,你能帮助他么?
【输入文件】
第一行包含两个整数N和M,分别表示矩形纸片的长和宽。接下来的N行包含一个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
【题目分析】
首先把矩阵转化一下,把横纵坐标和为偶数点的值取反,这样就转化成求最大的'0'或'1'矩阵。
我们只讨论对于0的求法,对1类似。
首先是最大正方形问题,这是一个经典的DP问题,f[i,j]表示以i,j为右下角的最大正方形的边长,那么a[i,j]=0时,f[i,j]=min(f[i-1,j-1],f[i-1,j],f[i,j-1])+1,a[i,j]=1时,f[i,j]=0,f数组中的最大值即为第一问的答案。
对于第二问,我们用一个单调栈来解决。
首先枚举最大矩形的下边界,对于每一个下边界维护两个(也可以说是两次)单调栈,一次正向,一次反向,用s[i,j]表示从a[i,j]开始向上最多能扩展出几个0,如果a[i,j]=1,那么s[i,j]=0,这样维护栈中元素的s值递增,每次元素出栈时就可以确定这个元素可以向左或向右扩展多少长度了,即找到了第一个s值小于该元素的位置,然后用每个元素的s值乘以向左向右扩展的长度和去更新第二问的答案就可以了。
由于每个元素最多进栈或出栈一次,每次的复杂度为O(n),这样总的时间复杂度就是O(n^2);
【代码实现】
1 program lyd1057;
2 var a:array[0..2000,0..2000]of 0..1;
3 f:array[0..2000,0..2000]of longint;
4 s:array[0..2000,0..2000]of longint;
5 z:array[0..2000]of longint;
6 l,r,p:array[0..2000]of longint;
7 ans1,ans2,i,j,m,n,k,t:longint;
8 function max(a,b:longint):longint;
9 begin
10 if a>b then exit(a)
11 else exit(b);
12 end;
13 function min(a,b:longint):longint;
14 begin
15 if a<b then exit(a)
16 else exit(b);
17 end;
18 procedure dp(o:longint);
19 var i,j:longint;
20 begin
21 for i:=1 to n do
22 if a[1,i]=o then s[1,i]:=1;
23 for i:=2 to m do
24 for j:=1 to n do
25 if a[i,j]=o then s[i,j]:=s[i-1,j]+1
26 else s[i,j]:=0;
27 for i:=1 to m do
28 for j:=1 to n do
29 begin
30 if a[i,j]=o then f[i,j]:=min(f[i-1,j-1],min(f[i-1,j],f[i,j-1]))+1
31 else f[i,j]:=0;
32 ans1:=max(ans1,f[i,j]);
33 end;
34 for i:=1 to m do
35 begin
36 fillchar(l,sizeof(l),0);
37 fillchar(r,sizeof(r),0);
38 t:=0;
39 for j:=1 to n do
40 begin
41 while (t>0)and(s[i,j]<s[i,z[t]]) do
42 begin
43 r[z[t]]:=j-z[t];
44 dec(t);
45 end;
46 if a[i,j]=o then
47 begin
48 inc(t);
49 z[t]:=j;
50 end;
51 end;
52 for j:=t downto 1 do
53 r[z[j]]:=n-z[j]+1;
54 t:=0;
55 for j:=n downto 1 do
56 begin
57 while (t>0)and(s[i,j]<s[i,z[t]]) do
58 begin
59 l[z[t]]:=z[t]-j;
60 dec(t);
61 end;
62 if a[i,j]=o then
63 begin
64 inc(t);
65 z[t]:=j;
66 end;
67 end;
68 for j:=t downto 1 do
69 l[z[j]]:=z[j];
70 for j:=1 to n do
71 if a[i,j]=o then
72 begin
73 p[j]:=l[j]+r[j]-1;
74 ans2:=max(ans2,p[j]*s[i,j]);
75 end;
76 end;
77 end;
78 begin
79 readln(m,n);
80 for i:=1 to m do
81 for j:=1 to n do
82 begin
83 read(a[i,j]);
84 if (i+j)and 1=0 then a[i,j]:=1-a[i,j];
85 end;
86 dp(0);
87 dp(1);
88 ans1:=sqr(ans1);
89 writeln(ans1);
90 writeln(ans2);
91 end.