bzoj 1057[ZJOI2007]棋盘制作 - 垂线法

1057: [ZJOI2007]棋盘制作

Time Limit: 20 Sec  Memory Limit: 162 MB

Description

 

  国际象棋是世界上最古老的博弈游戏之一,和中国的围棋、象棋以及日本的将棋同享盛名。据说国际象棋起源
于易经的思想,棋盘是一个8*8大小的黑白相间的方阵,对应八八六十四卦,黑白对应阴阳。而我们的主人公小Q,
正是国际象棋的狂热爱好者。作为一个顶尖高手,他已不满足于普通的棋盘与规则,于是他跟他的好朋友小W决定
将棋盘扩大以适应他们的新规则。小Q找到了一张由N*M个正方形的格子组成的矩形纸片,每个格子被涂有黑白两种
颜色之一。小Q想在这种纸中裁减一部分作为新棋盘,当然,他希望这个棋盘尽可能的大。不过小Q还没有决定是找
一个正方形的棋盘还是一个矩形的棋盘(当然,不管哪种,棋盘必须都黑白相间,即相邻的格子不同色),所以他
希望可以找到最大的正方形棋盘面积和最大的矩形棋盘面积,从而决定哪个更好一些。于是小Q找到了即将参加全
国信息学竞赛的你,你能帮助他么?

Input

  第一行包含两个整数N和M,分别表示矩形纸片的长和宽。接下来的N行包含一个N * M的01矩阵,表示这张矩形
纸片的颜色(0表示白色,1表示黑色)。

Output

  包含两行,每行包含一个整数。第一行为可以找到的最大正方形棋盘的面积,第二行为可以找到的最大矩形棋
盘的面积(注意正方形和矩形是可以相交或者包含的)。

Sample Input

3 3
1 0 1
0 1 0
1 0 0

Sample Output

4
6

HINT

 

N, M ≤ 2000

 

首先有一个很奇妙的变换, 因为题目要求的是黑白相间的,我们对 横坐标与纵坐标和为偶数的格子颜色取反

这样题目就转化为了颜色相同

我们可以n * m 的时间预处理出  le[i][j] 和 ri[i][j] (分别表示 在第 i 行 第 j 列的格子向左和向右延伸所有格子颜色相同的最远处)

然后对于每列从上往下扫

l[i][j] = min(l[i - 1][j], le[i][j]), r[i][j] = min(r[i - 1][j], ri[i][j])

表示当前这个上边界固定的矩形最左端和最右端

如果一个矩形的面积是最大的,它的上边界一定会触碰到一个障碍,所以从每个障碍往下计算一定不会漏解

 

  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstring>
  4 #include <algorithm>
  5 #define LL long long
  6 
  7 using namespace std;
  8 
  9 const int MAXN = 2e3 + 10;
 10 int N, M;
 11 int ans1 = 0, ans2 = 0;
 12 int map[MAXN][MAXN];
 13 int le[MAXN][MAXN], ri[MAXN][MAXN], h[MAXN][MAXN];
 14 int l[MAXN][MAXN], r[MAXN][MAXN];
 15 inline LL read()
 16 {
 17     LL x = 0, w = 1; char ch = 0;
 18     while(ch < '0' || ch > '9') {
 19         if(ch == '-') {
 20             w = -1;
 21         }
 22         ch = getchar();
 23     }
 24     while(ch >= '0' && ch <= '9') {
 25         x = x * 10 + ch - '0';
 26         ch = getchar();
 27     }
 28     return x * w;
 29 }
 30 
 31 void work(int x)
 32 {
 33     for(int i = 1; i <= N; i++) {
 34         int last = 0;
 35         for(int j = 1; j <= M; j++) {
 36             if(map[i][j] == x) {
 37                 last = j;
 38             }
 39             le[i][j] = last;
 40         }
 41         last = M + 1;
 42         for(int j = M; j >= 1; j--) {
 43             if(map[i][j] == x) {
 44                 last = j;
 45             }
 46             ri[i][j] = last;
 47         }
 48     }
 49     /*for(int i = 1; i <= N; i++) {
 50         for(int j = 1; j <= M; j++) {
 51             cout<<le[i][j]<<" "<<ri[i][j]<<" ";
 52         }
 53         cout<<endl;
 54     }
 55     cout<<endl<<endl;*/
 56     for(int j = 1; j <= M; j++) {
 57         r[0][j] = M + 1;
 58     }
 59     for(int j = 1; j <= M; j++) {
 60         for(int i = 1; i <= N; i++) {
 61             if(map[i][j] == x) {
 62                 h[i][j] = 0;
 63                 l[i][j] = 0, r[i][j] = M + 1;
 64             } else {
 65                 h[i][j] = h[i - 1][j] + 1;
 66                 l[i][j] = max(le[i][j], l[i - 1][j]);
 67                 r[i][j] = min(ri[i][j], r[i - 1][j]);
 68                 int w = r[i][j] - l[i][j] - 1;
 69                 //cout<<h[i][j]<<" "<<l[i][j]<<" "<<r[i][j]<<" ";
 70                 ans1 = max(ans1, min(w, h[i][j]) * min(w, h[i][j]));
 71                 ans2 = max(ans2, h[i][j] * w);
 72             }
 73         }
 74         //cout<<endl;
 75     }
 76     //cout<<endl;
 77 }
 78 int main()
 79 {
 80     N = read(), M = read();
 81     for(int i = 1; i <= N; i++) {
 82         for(int j = 1; j <= M; j++) {
 83             map[i][j] = read();
 84             if((i + j) % 2 == 0) {
 85                 map[i][j] = map[i][j] ^ 1;
 86             }
 87         }
 88     }
 89     /*for(int i = 1; i <= N; i++) {
 90         for(int j = 1; j <= M; j++) {
 91             cout<<map[i][j]<<" ";
 92         }
 93         cout<<endl;
 94     }*/
 95     work(1);
 96     work(0); 
 97     printf("%d\n%d\n", ans1, ans2);
 98     return 0;
 99 }
100 
101 
102 /*
103 
104 3 3
105 1 0 1
106 0 1 0
107 1 0 0
108 
109 
110 */
View Code

 

posted @ 2018-03-24 15:56  大财主  阅读(177)  评论(0编辑  收藏  举报