1. 程式人生 > >翻轉長方形 (不知名oj中一道個人私題)--單調棧維護最大子矩形

翻轉長方形 (不知名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\n",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\n",nxt[j]);
		}
// 		printf("\n");
        solve();
	}
	printf("%d\n", ans);
	return 0;	
}