CodeVS1428 棋盘制作

http://codevs.cn/problem/1428/

题意:给定一个N行M列的01矩阵,要求找出元素数最多的子正方阵和子矩阵,使得其中任意两个相邻元素均不同,N,M<=2000。

任意相邻两个元素均不同就意味着,要么奇数位上全0偶数位上全1,要么奇数位上全1偶数位上全0,这里的奇偶指的是行数和列数之和的奇偶性。

这个问题和经典的最大全0子矩阵有着很大的相似性,我们可以考虑转化过去。先来讲一讲最大全0子矩阵的做法。

建立二维数组h[],h[i][j]表示从左向右到元素[i][j]连续的0的数量。如下面这个01矩阵:

0 0 0

0 0 0

0 0 1

h[]数组的值应该为:

1 2 3

1 2 3

1 2 0

这个数组的值可以用简单的递推O(n^2)内求出。

接下来,对于每个h[i][j],求出其正上方和正下方各到何处仍有连续的h[][j]不小于h[i][j],如上面的例子,对于h[2][2]=2,向上可以拓展到h[1][2],向下可以拓展到h[3][2]。

设向上连续到h[l][j],向下连续到h[r][j],则得到了一个(r-l+1)行h[i][j]列的矩阵,用乘积对当前最大值进行修改即可。

但是,如果要直接扫描求l和r,在最坏情况下,当h[][j]单调上升时,从每个[][j]开始向下都会扫描到最下端,这样单次扫描O(n),总时间复杂度O(n^3),还是无法满足要求。

事实上,我们要寻找的就是对于每个元素,正上方和正下方第一次出现h[]值小于当前位置h[]值得位置,将该位置加1或减1即得到l和r。

这里我们要用到一种叫做单调栈的结构,保持栈底到栈顶的全部元素都是单调的。

每当有新元素要入栈时,若直接入栈不能保证单调性,则不断将栈顶元素弹出直到可以入栈再入栈。

如序列1,0,3,2,5,4,要找出从4向左第一个比4小的元素,则先将前5个数入栈得到0,2,5,要将4入栈,需先将5弹出,则弹出后的栈顶元素2即为要寻找的元素。

由于每个元素最多进出栈一次,而找元素和维护同时进行的,因此对于每一列的各元素,寻找l和r是线性O(n)的,因而整个算法是O(n^2)的。

接下来将上述算法应用到题目中,还是要考虑到满足条件的01子矩阵矩阵的两种情况:

对于前者,可以先将所有偶数位上的数取反然后做一次最大全0矩阵,对于后者则是对奇数位操作,这样只要在修改后各做一次即可。

关于子正方阵,只要将每次得到的长和宽中取较小值平方即可。

代码:

#include<cstdio>
#define rpt(i,l,r) for(i=l;i<=r;i++)
#define rpd(i,r,l) for(i=r;i>=l;i--)
int a[2015][2015],b[2015][2015],l[2015][2015],r[2015][2015];
int n,m,i,j,ans1,ans2;
int st[2015],tp;
int max(int x,int y){
	if(x>y) return x;
	else return y;
}
int min(int x,int y){
	if(x<y) return x;
	else return y;
}
void work(){
	rpt(i,1,n) rpt(j,1,m){
		if(a[i][j]==0) b[i][j]=0;
		else if(j==1) b[i][j]=1;
		else b[i][j]=b[i][j-1]+1;
	}
	rpt(j,1,m){
		st[tp=0]=0;
		rpt(i,1,n){
			while(b[i][j]<=b[st[tp]][j]) tp--;
			l[i][j]=st[tp]+1;
			st[++tp]=i;
		}
		st[tp=0]=n+1;
		rpd(i,n,1){
			while(b[i][j]<=b[st[tp]][j]) tp--;
			r[i][j]=st[tp]-1;
			st[++tp]=i;
		}
	}
	rpt(i,1,n) rpt(j,1,m){
		ans1=max(ans1,min(b[i][j],r[i][j]-l[i][j]+1));
		ans2=max(ans2,b[i][j]*(r[i][j]-l[i][j]+1));
	}
}
int main(){
	scanf("%d%d",&n,&m);
	rpt(i,1,n) rpt(j,1,m){
		scanf("%d",&a[i][j]);
		if(i+j&1) a[i][j]=1-a[i][j];
	}
	rpt(j,1,m) b[0][j]=b[0][n+1]-1;
	ans1=ans2=0;
	work();
	rpt(i,1,n) rpt(j,1,m) a[i][j]=1-a[i][j];
	work();
	printf("%d\n%d\n",ans1*ans1,ans2);
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
信息数据从传统到当代,是一直在变革当中,突如其来的互联网让传统的信息管理看到了革命性的曙光,因为传统信息管理从时效性,还是安全性,还是可操作性等各个方面来讲,遇到了互联网时代才发现能补上自古以来的短板,有效的提升管理的效率和业务水平。传统的管理模式,时间越久管理的内容越多,也需要更多的人来对数据进行整理,并且数据的汇总查询方面效率也是极其的低下,并且数据安全方面永远不会保证安全性能。结合数据内容管理的种种缺点,在互联网时代都可以得到有效的补充。结合先进的互联网技术,开发符合需求的软件,让数据内容管理不管是从录入的及时性,查看的及时性还是汇总分析的及时性,都能让正确率达到最高,管理更加的科学和便捷。本次开发的高校科研信息管理系统实现了操作日志管理、字典管理、反馈管理、公告管理、科研成果管理、科研项目管理、通知管理、学术活动管理、学院部门管理、科研人员管理、管理员管理等功能。系统用到了关系型数据库中王者MySql作为系统的数据库,有效的对数据进行安全的存储,有效的备份,对数据可靠性方面得到了保证。并且程序也具备程序需求的所有功能,使得操作性还是安全性都大大提高,让高校科研信息管理系统更能从理念走到现实,确确实实的让人们提升信息处理效率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值