2020牛客暑期多校训练营(第九场) The Escape Plan of Groundhog

原题
题目描述
一堆桌子被排列成 N × M N×M N×M的矩形, a [ i ] [ j ] = 1 a[i][j]=1 a[i][j]=1表示位置 ( i , j ) (i,j) (ij)处有桌子,否则就没有。为了不被老师抓住,他要藏在一个矩形下,这个矩形要满足这些条件 : :
1 、 1、 1该子矩形的四条边上没有空位;
2 、 2、 2子矩形中的空位与桌子的数量之差不超过 1 1 1(不包括侧面的桌子) ; ;
3 、 3、 3子矩形的长度和宽度必须大于 1 1 1
有多少个子矩形可以满足要求 ? ?
输入描述 : :
输入包含两个整数 N N N M M M,范围为 [ 1 , 500 ] [1,500] [1,500],中间用空格隔开。
然后是 N N N行,每行包含 M M M个字符来描述矩形 ( a [ i ] [ j ] ∈ 0 , 1 ) (a[i][j]∈{0,1}) a[i][j]0,1
样例1
输入

4 4
1 1 1 1
1 0 1 1
1 1 0 1
1 1 1 1

输出

3

说明

There're two 2*2 rectangle full of "1",and the whole 4*4 rectangle .

样例2
输入

5 5
1 0 1 1 1
1 0 1 0 1
1 1 0 1 1
1 0 0 1 1
1 1 1 1 1

输出

3

思路
因为本题的数据范围还是比较小的, N N N M M M ≤ 500 ≤500 500,所以可以考虑时间复杂度为 O ( O( O( n n n3 ) ) )的算法。
首先,我们会想到一种 O ( n O(n O(n4 ) ) )的算法 : :
枚举矩阵左上的端点和右下的端点,然后判断一下矩阵周围一圈是不是 1 1 1,有一个不是 1 1 1就退出搜索。然后判断一下矩阵除了最外圈的 1 1 1 0 0 0 1 1 1数量的差值是否 ≤ 0 ≤0 0,如果是,累加到 a n s ans ans上。
显然这就是一个单纯的暴力,时间复杂度明显不对,所以还得优化。
首先,我们可以原来 01 01 01的矩阵转化为 − 1 -1 1 1 1 1的矩阵,然后求一下前缀和。
如果求出一个区间内的数字和的绝对值 ≤ 1 ≤1 1,则表示这是一个合法的区间。
我们可以枚举上下行边界,对于每一列扫一遍,用前缀和维护即可,维护时不能算上这个矩阵周围的 1 1 1
枚举列的时候,如果这一列也是 1 1 1,就可以统计进去。
代码

#include
#define ll long long 
using namespace std;
const int inf=1<<15;
int a[505][505],b[505][505],c[250005],d[505],n,m,x=1;
int ans;
int main()
{
	d[0]=inf;scanf("%lld%lld",&n,&m);
	for(int i=1;i<=n;i++)for(int j=1;j<=m;j++){scanf("%lld",&a[i][j]);if(!a[i][j])a[i][j]=-1;b[i][j]=b[i-1][j]+a[i][j];}
	for(int i=1;i<=n;i++)
		for(int j=i+1;j<=n;j++,x=1)
		{
			for(int k=1;k<=m;k++)
				if(a[i][k]^1||a[j][k]^1)
				{
					for(int l=x;l<=k;l++)if(b[j][l]-b[i-1][l]==j-i+1)c[d[l]]--;
					x=k+1,d[k]=inf;
				}
				else
				{
					if(b[j][k]-b[i-1][k]==j-i+1)ans+=c[d[k-1]]+c[d[k-1]+1]+c[d[k-1]-1];
	                d[k]=d[k-1]+b[j-1][k]-b[i][k];
	                if(b[j][k]-b[i-1][k]==j-i+1)c[d[k]]++;
				}
			for(int k=x;k<=m;k++)if(b[j][k]-b[i-1][k]==j-i+1)c[d[k]]--;
		}
	printf("%d\n",ans);
}

你可能感兴趣的:(2020牛客暑期多校训练营(第九场) The Escape Plan of Groundhog)