【最大01子矩阵二合一】TYVJ1542 棋盘制作 POJ1964 City Game

(标题里写个关键词骗点击)

这俩题实际上是一个东西,就叫最大01子矩阵(瞎叫的)。

大致是这样的模型:有一个由0和1组成的矩阵,然后要求最大的一个矩阵使得其中只包含0(1同理)。

那么这个怎么做呢?

当然是可以做的(废话),结合求最大正方形的方法,我们可以这样乱搞:

首先预处理出所有点可以向上扩展的最长距离和向左扩展的最长距离——这个是N*M的。

然后对每个点,从这个点出发向左扩展,记录一个到当前位置纵向的最小高度,每次更新答案,直至不能扩展为止——这步是N*M*K(K为向左扩展距离)的,也就是3方的、

显然不管是TYVJ还是POJ,这个算法都是过不了的。

经大犇指点,我了解到这个是可以用一个叫单调栈的东西优化的……

那这个单调栈又是个什么呢?

简单地说,就是维护一个单调递增的向上扩展距离(也就是纵向高度)的序列,然后每次退栈的时候更新答案。

这个正确性很显然:如果当前段的高度比之前段的高度高,那么算之前段高度的最大值使必然要包含当前段,否则一定不最优。

这样就可以在N*M的时间里搞的。

具体实现还是要看程序,本蒟蒻也是盯着程序看了半天才略懂的。

 

TYVJ 1542

Code:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <cstdlib>

using namespace std;

int sq[2001][2001],a[2001][2001],s[2001],t[2001];
int n,m,ans=0;

void work(){
	memset(sq,0,sizeof(sq));
	for (int i=1;i<=n;i++)
	    for (int j=1;j<=m;j++)
			sq[i][j]=(a[i][j])?sq[i-1][j]+1:0;
	int p=1;
	for (int i=1;i<=n;i++){
		s[p]=-1;t[p]=0;
		int w;
		for (int j=1;j<=m;j++){
			int h=sq[i][j];w=0;
			while (s[p]>h){
				w+=t[p];
				ans=max(ans,w*s[p--]);
			}
			s[++p]=h;
			t[p]=w+1;
		}
		w=0;
		while (p-1){
			w+=t[p];
			ans=max(ans,w*s[p--]);
		}
	}
}

int main(){
	scanf("%d %d\n",&n,&m);
	for (int i=1;i<=n;i++)
	    for (int j=1;j<=m;j++)
	        scanf("%d",&a[i][j]);
	memset(sq,0,sizeof(sq));
	for (int i=1;i<=n;i++)
		sq[i][m]=1;
	for (int i=1;i<=m;i++)
	    sq[n][i]=1;
	for (int i=n-1;i>0;i--)
	    for (int j=m-1;j>0;j--){
			if ((a[i][j]!=a[i+1][j])&&(a[i][j]!=a[i][j+1])&&(a[i][j]==a[i+1][j+1]))
				sq[i][j]=min(sq[i+1][j],min(sq[i][j+1],sq[i+1][j+1]))+1;
			else sq[i][j]=1;
			ans=max(ans,sq[i][j]);
		}
	ans*=ans;
	printf("%d\n",ans);
	ans=0;
	for (int i=1;i<=n;i++)
	    for (int j=1;j<=m;j++)
	        if ((i+j)&1) a[i][j]^=1;
	work();
	for (int i=1;i<=n;i++)
	    for (int j=1;j<=m;j++)
	        a[i][j]^=1;
	work();
	printf("%d\n",ans);
	return 0;
}

 POJ 1964

Code:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <cstdlib>

using namespace std;

int sq[1001][1001],a[1001][1001],s[1001],t[1001];
int n,m,ans=0;

void work(){
	memset(sq,0,sizeof(sq));
	for (int i=1;i<=n;i++)
	    for (int j=1;j<=m;j++)
			sq[i][j]=(a[i][j])?sq[i-1][j]+1:0;
	int p=1;
	for (int i=1;i<=n;i++){
		s[p]=-1;t[p]=0;
		int w;
		for (int j=1;j<=m;j++){
			int h=sq[i][j];w=0;
			while (s[p]>h){
				w+=t[p];
				ans=max(ans,w*s[p--]);
			}
			s[++p]=h;
			t[p]=w+1;
		}
		w=0;
		while (p-1){
			w+=t[p];
			ans=max(ans,w*s[p--]);
		}
	}
}

int main(){
	int k;
	scanf("%d",&k);
	while (k){
		scanf("%d %d",&n,&m);
		char c;
		memset(a,0,sizeof(a));
		for (int i=1;i<=n;i++)
		    for (int j=1;j<=m;j++){
				scanf("%c",&c);
				while ((c!='R')&&(c!='F')) scanf("%c",&c);
				if (c=='F') a[i][j]=1;
		    }
		ans=0;
		work();
		printf("%d\n",ans*3);
		k--;
	}
	return 0;
}

 

posted @ 2012-06-10 21:27  JS_Shining  阅读(1045)  评论(0编辑  收藏  举报