参考博客:
http://blog.youkuaiyun.com/jzq233jzq/article/details/71438739
题意 :
给你一个0/1矩阵,找出最大的子矩阵。子矩阵的要求是任意相邻的两个数字必须不同。
思路:
看了题解想到可以转化成最大全0/1子矩阵,用到了单调栈活着悬线法。(悬线法没学会。。
首先是转化:就是把横纵坐标之和为基数的(偶数也可以,看心情,今天心情不好,我要基数)就把1变0,0变1。
接下来单调栈找出最大全..子矩形:
通过这篇文章学到了单调栈的基本原理http://calify.lofter.com/post/1d2d563b_9b67539
感觉单调栈好神奇啊,然而为了啃这个单调栈已经很久没能A掉一道题目了,啃了一个星期。
简单说来就是
将给的数据依次压入栈中,当要压入的数据 < 栈顶的数据 , 就准备弹出栈顶元素,但是在弹出的时候要更新一下栈顶数据(作为一条边)的面积,再更新一下上域(指的是向上可以扩到哪里)
(PS :明白上域的可以略过这里~
我一开始想的是两次for确定一个点,再来两次for看看能往上往下都可以扩展到多远。n^4,n是2000,
就是 8 * 10^12.我不知道20s的时限会不会超时,所以就去学了一下单调栈。比如:
1 1 1 0 0
1 1 1 1 0
1 1 1 0 0
1 1 1 1 1
1 1 0 0 0
1 1 0 0 0
1 1 0 0 0
转化成
3 2 1 0 1
4 3 2 1 0
3 2 1 0 0
5 4 3 2 1
2 1 0 0 0
2 1 0 0 0
2 1 0 0 0
第三行第一列往上可以扩展到第一行,往下可以扩展到第四行,面积3 * 4 = 12.
而单调栈只需要一次,因为当你第三行的3想进去,4就要弹出来,弹出来就要马上将要压入的第三行的数据3的行存为2而不是3. 这样当第五行的2进来迫使第三行的3弹出去的时候就是(5 - 2)*3,这里的3行指的是2-4行,因为4行弹出去的时候3是小的,3可以扩招到4的那一行,所以将3的行存为2,就相当于往上扩展了1行。
那可是正确的应该是网上扩展两行啊,这里因为第一行有一个相同的3,所以用第五行的2的行(5)去减第一行的3的行(1)就是4,代表着3可以扩展4行,这里考虑会不会落下情况,答:不会!因为有相同的数据会使得最终得到正确的扩展行,如果数据是唯一的,他扩展的行也是不需要再网上扩展的,因为,数据唯一,代表着第一行是0,1,2,4这四个数字中的一个,如果是0,1,2就没必要再往上扩展,如果是4,就更不用扩展了,因为到时候3会到栈顶(里面只有3,此时之前弹出的是两个4)就是说3的行为1,再继续,加入行为4的5,再继续加入第五行的2时,就要弹出啦,弹出3的时候就是3 * (5 - 1) = 12.)-----------PS结束
现在有了上下扩展域,就可以计算了。
坑点是:
1.需要加一个0来使每一列彻底弹出(按照一列一列来更新的)
2.还要注意按照连续的1来计算一次,按照连续的0来计算一次。
哎,实在抱歉没有描述清楚。
对不起大家。
上代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 2050;
int n, m;
bool a[N][N];
int sum = 0;
int ans1 = 0;
int ans2 = 0;
int b[N][N];
struct Node
{
int k;
int x;
} s[N];
inline int fang(int x)
{
return x * x;
}
inline void work(int k, int x)
{
int p = k;
for(; sum && x < s[sum].x; k = s[sum --].k)
{
ans1 = max(ans1, fang(min(s[sum].x, (p - s[sum].k))));
ans2 = max(ans2, s[sum].x * (p - s[sum].k));
}
s[++sum].k = k;
s[sum].x = x;
}
int main()
{
// freopen("in.txt", "r", stdin);
scanf("%d%d", &n, &m);
memset(a, 0, sizeof(a));
for(int i = 1; i <= n; i ++)
{
for(int j = 1; j <= m; j ++)
{
scanf("%d", &a[i][j]);
if((i + j)&1)
{// a[i][j] = ~a[i][j];
a[i][j] ^= 1;
}
getchar();
}
}
// printf("\n");
memset(b, 0, sizeof(b));
for(int i = 1; i <= n; i ++)
{
for(int j = m; j >= 1; j --)
{
b[i][m + 1] = 0;
b[i][j] = a[i][j] ? b[i][j + 1] + 1 : 0;
}
}
for(int j = 1; j <= m; j ++)
{
work(n + 1, 0);
sum = 0;
for(int i = 1; i <= n; i ++)
{
work(i, b[i][j]);
}
}
memset(b, 0, sizeof(b));
for(int i = 1; i <= n; i ++)
{
for(int j = m; j >= 1; j --)
{
b[i][m + 1] = 0;
b[i][j] = a[i][j] == 0? b[i][j + 1] + 1 : 0;
}
}
for(int j = 1; j <= m; j ++)
{
work(n + 1, 0);
sum = 0;
for(int i = 1; i <= n; i ++)
{
work(i, b[i][j]);
}
}
printf("%d\n%d\n", ans1, ans2);
}