翻转长方形 (不知名oj中一道个人私题)--单调栈维护最大子矩形

怎么分析这道题呢?

首先 ,我们注意到一点:
不管怎么操作,任意一个2*2方格中的 "#"个数的奇偶性是不变的。

所以,如果一个2*2方格中有奇数个"#",这个方格里的格子永远不可能变成同一种颜色。

并且,如果一个矩形中,所有2*2方格中有偶数个"#",那么它一定可以能变成只有一种颜色的矩形。

为什么?

先把这个矩形的第一行和第一列都变成同一种颜色,这个操作任何矩形都能做到。

如果这个矩形中,所有2*2方格中有偶数个"#",那么在左上角的2*2方格一定都是"#"。(一直是偶数个“#”)。这样在它右边的2*2方格同样一定都是"#"。以此类推,它一定可以能变成只有一种颜色的矩形。

然后我们再来维护最大的不包含有奇数个"#"的2*2方格的矩形。

这是一个经典问题,我们可以用单调栈来解决。

#include<bits/stdc++.h>
using namespace std;

const int maxn=2005;
int h, w, len[maxn], up[maxn], down[maxn], nxt[maxn], ans;
char s[maxn][maxn];

int bad(int y, int x){
	int ans = (s[y][x] == '#')^
	(s[y][x+1] == '#')^
	(s[y+1][x] == '#')^
	(s[y+1][x+1] == '#');
	return ans;
}
 
void solve(){
	stack<int> stk;
	for(int i=1;i<=h;i++){
		while(!stk.empty() && len[stk.top()] >= len[i])
			stk.pop();
		if(stk.empty())up[i]=0;
		else up[i]=stk.top();
		if(len[i] != 0)stk.push(i);
	}
	while(!stk.empty()) stk.pop();
	for(int i=h;i>=1;i--){
		while(!stk.empty() && len[stk.top()] >= len[i])
			stk.pop();
		if(stk.empty())down[i]=h;
		else down[i]=stk.top();
		if(len[i] != 0)stk.push(i);
	}
	for(int i=1;i<=h;i++){
		ans = max(ans, len[i]*(down[i]-up[i]));
// 		printf("%d:%d %d %d
",i,len[i],down[i],up[i]);
	}
}
int main()
{
	scanf("%d%d",&h,&w);
	for(int i = 1; i <= h; i++)
		scanf("%s", s[i]+1);
	for(int i = 1; i <= w; i++){
		for(int j = 1; j <= h; j++){
		    if(nxt[j]) len[j] = 1;
		    else len[j]++;
			if(bad(j,i) && i!=w && j!=h) nxt[j] = 1;
			else nxt[j] = 0;
// 			printf("%d
",nxt[j]);
		}
// 		printf("
");
        solve();
	}
	printf("%d
", ans);
	return 0;	
}

原文地址:https://www.cnblogs.com/Eroad/p/9373475.html