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);

【代码实现】

Code
 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.

 

转载于:https://www.cnblogs.com/whitecloth/archive/2012/04/11/2443003.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值