⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 noj 1017 最大零矩阵.txt

📁 NUAA ACM OJ源码
💻 TXT
字号:
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
using namespace std;

/*NOJ 1017
算法简介:
设h[i, j]为元素a[i j]的高度,其定义如下:
若a[i j]为1,则h[i, j]=0,否则定义为从该元素开始在同一列上向上延伸的连续的零(0)的个数.考虑面积最大的全零(0)子矩阵的最后一行,则要求的面积最大的全零(0)子矩阵的高度必为其最后一行所有零(0)元素中高度最小的元素的高度,否则该子阵必不是最大的,与假设矛盾.
再定义l[i, j]为从第i行第j列开始向左延伸高度均不小于h[i, j]的最左一列的列号,即从该列开始至第j列的每个元素的高度均不小于h[i,j],同样定义r[i, j]为从第i行第j列开始向右延伸高度均不小于h[i, j]的最右一列的列号,即从第j列开始至该列的每个元素的高度均不小于h[i,j],
则底边包含元素a[i,j]的面积最大的全零子阵的面积为(r[i,j]-l[i,j]+1)*h[i,j],
而h(i,j),l(i,j),r(i,j)可以逐行递推,递推的方法为增加一个元素值全1的第0行,这不影响问题的解,则根据定义h[0,j]=0, l[0,j]=1,r[0,j]=n,其中1<=j<=n,此为边界条件,且对任意的非零元素上式均适用;
再假设第i行包含第j列的最长的连续全零段最左为第c1列,最右为第c2列,即第i行从第c1列开始至第c2列为止的每个元素的高度均不小于h[i,j],则若递推式为:
h[i,j]=h[i-1,j]+1, l[i,j]=max(l[i-1,j],c1), r[i,j]=min(r[i-1,j,c2]).
对任意的i,j求出(r[i,j]-l[i,j]+1)*h[i,j],其中的最大值即问题的解. 

*/

#define NMAX 2002
int ju[NMAX][NMAX];
int high[NMAX][NMAX];
int l[NMAX][NMAX];
//l[i][j],第i行以第j列为基准,只要高度小于基准,就向左延伸,最后延伸到的列数
int r[NMAX][NMAX];
//l[i][j],第i行以第j列为基准,只要高度小于基准,就向右延伸,最后延伸到的列数

int cal(int num)
{
	int i,j,k,min=0,c1,c2;
	//c1,是在第i行包含第j列的全零序列的最左列数
	//c2,是在第i行包含第j列的全零序列的最右列数
	for(i=0;i<=num;i++) high[0][i]=0;
	for(i=1;i<=num;i++)
	{
		for(j=1;j<=num;j++)
		{	//计算零列高度
			if(ju[i][j]==1) high[i][j]=0;
			else if(ju[i-1][j]==0) high[i][j]=high[i-1][j]+1;
			else high[i][j]=1;
		}
	}
	for(j=0;j<=num;j++)
	{	//初始化第0行的l[][],r[][]值
		l[0][j]=1;
		r[0][j]=num;
	}
	for(i=1;i<=num;i++)
	{
		//计算l[][],r[][]值
		j=1;c1=1;
		while(j<=num)
		{
			while(ju[i][j]==1)
			{
				l[i][j]=1;//高度为0,可知其向左能延伸到第1列
				j++;
				c1=j;//这个c1显然不合法,修改
			}
			//l[i][j]取l[i-1][j]和c1间的最大值
			if(l[i-1][j]>c1) l[i][j]=l[i-1][j];
			else l[i][j]=c1;
			j++;
		}
		c2=num;k=num;
		while(k>=1)
		{
			while(ju[i][k]==1)
			{
				r[i][k]=num;//高度为0,可知其向右延伸到第num列
				k--;
				c2=k;//这个c2显然不合法,修改
			}
			//r[i][j]取r[i-1][j]和c2间的最小值
			if(r[i-1][k]>c2) r[i][k]=c2;
			else r[i][k]=r[i-1][k];
			k--;
		}
		//max{high[i][j]*(r[i][j]-l[i][j]+1)&&ju[i][j]==0},为所求答案
		for(j=1;j<=num;j++)
		if(min<high[i][j]*(r[i][j]-l[i][j]+1)&&ju[i][j]==0) min=high[i][j]*(r[i][j]-l[i][j]+1);
	}
	return min;
}

int main()
{
	int num,i,j;
	while(scanf("%d",&num)!=EOF)
	{
		for(i=1;i<=num;i++)
		{
			for(j=1;j<=num;j++)
			scanf("%d",&ju[i][j]);
		}
		cout<<cal(num)<<endl;
	}
	return 0;
}
/*
void printh(int num)
{
	int i,j;
	cout<<endl;
	for(i=1;i<=num;i++)
	{
		for(j=1;j<=num;j++)
			cout<<high[i][j]<<" ";
		cout<<endl;
	}
	cout<<endl;
}

void printl(int num)
{
	int i,j;
	cout<<endl;
	for(i=1;i<=num;i++)
	{
		for(j=1;j<=num;j++)
			cout<<l[i][j]<<" ";
		cout<<endl;
	}
	cout<<endl;
}

void printr(int num)
{
	int i,j;
	cout<<endl;
	for(i=1;i<=num;i++)
	{
		for(j=1;j<=num;j++)
			cout<<r[i][j]<<" ";
		cout<<endl;
	}
	cout<<endl;
}
*/

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -