【2018/10/02测试T2】【WOJ 4019】矩阵分组

【题目】

题目描述:

有 N 行 M 列的矩阵,每个格子中有一个数字,现在需要你将格子的数字分为 A , B 两部分

要求:

1、每个数字恰好属于两部分的其中一个部分

2、每个部分内部方块之间,可以上下左右相互到达,且每个内部方块之间可以相互到达,且最多拐一次弯

如:

AAAAA     AAAAA     AAAAA
AABAA     BaAAA     AAABB
ABBBA     BBAAA     AAABB
AABAA     BaAAA     ABBBB
AAAAA     AAAAA     BBBBB

 (1)       (2)       (3)

其中(1)(2)是不允许的分法,(3)是允许的分法。在(2)中,a 属于 A 区域,这两个 a 元素之间互相到达,但是不满足只拐一次弯到达。

问:对于所有合法的分组中,A 区域和 B 区域的极差,其中极差较大的一个区域最小值是多少

提示:极差就是区域内最大值减去最小值。

输入格式:

第一行两个正整数 N,M

接下来 N 行,每行 M 个自然数 a_{i,j} 表示权值

输出格式:

输出一行表示答案

样例数据:

输入

4 4
1 12 6 11
11 4 2 14
10 1 9 20
4 17 13 10

输出

11

备注:

【样例解释】

1 12 6          11
11 4 2          14
10 1 9          20
4         17 13 10

分法不唯一,如图是一种合法的分法。左边部分极差 12-1=11,右边一块极差 20-10=10,所以答案取这两个中较大者 11。没有别的分法,可以使答案更小。

【数据规模与约定】

测试点 n , m范围
1,2 n ≤ 10,m ≤ 10
3,4 n = 1,m ≤ 2000
5,6,7 n ≤ 200,m ≤ 200
8,9,10 n ≤ 2000,m ≤ 2000

所有权值 1 ≤ a_{i,j}  ≤ 10^9

【分析】

看到“极差较大的一个区域最小值是多少”这样的字眼的时候其实就知道这道题多半是二分

但是想到二分之后,check 中间值貌似又无从下手

于是在考场上打了个 20 分暴力就没去管它了

话不多说,开始讲正解,正解做法也就是二分,不过这个 check 比较巧妙

很容易想到的是,对于一个合法的分法,A 和 B 应该是呈阶梯状

设 A 分布在左上角,将矩阵旋转三次就可以得到所有的情况,因此写一个 check 函数就行

设最大值在 A 区域,那么我们从第一行开始找到第一个与最大值差值不在大于 mid 的位置 p 

在第二行不超过 p-1 的位置同样找一个这样的边界, 以下的所有行都这样弄

这样子我们就将矩阵划分为了两个区域,我们枚举出来的左边区域一定满足条件,那么我们再判断右区域满不满足(与最小值的差值在条件范围内,也即都小于等于 mid

把旋转的四种情况都判断一下就可以得出答案

【代码】

#include<cctype>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 2005
using namespace std;
int Read()
{
	int x=0;
	char c=getchar();
	while(!isdigit(c))
	  c=getchar();
	while(isdigit(c))
	{
		x=(x<<1)+(x<<3)+(c^'0');
		c=getchar();
	}
	return x;
}
int n,m,maxn,minn;
int rec[N],a[10][N][N];
bool Check(int id,int mid)
{
	int i,j;
	if(!(id&1))
	  swap(n,m);
	rec[0]=m;
	for(i=1;i<=n;++i)
	{
		for(j=1;j<=rec[i-1];++j)
		  if(maxn-a[id][i][j]>mid)
		    break;
		rec[i]=j-1;
	}
	for(i=1;i<=n;++i)
	  for(j=rec[i]+1;j<=m;++j)
	    if(a[id][i][j]-minn>mid)
	    {
	    	if(!(id&1))
			  swap(n,m);
	    	return false;
	    }
	if(!(id&1))
	  swap(n,m);
	return true;
}
bool check(int mid)
{
	if(Check(1,mid))  return true;
	if(Check(2,mid))  return true;
	if(Check(3,mid))  return true;
	if(Check(4,mid))  return true;
	return false;
}
int main()
{
//	freopen("matrix.in","r",stdin);
//	freopen("matrix.out","w",stdout);
	int i,j,k;
	maxn=0,minn=1e+9;
	n=Read(),m=Read();
	int x1=1,y1=1,x2=1,y2=n,x3=n,y3=m,x4=m,y4=1;
	for(i=1;i<=n;++i)
	{
		for(j=1;j<=m;++j)
		{
			k=Read();
			maxn=max(maxn,k);
			minn=min(minn,k);
			a[1][x1][y1++]=k;
			a[2][x2++][y2]=k;
			a[3][x3][y3--]=k;
			a[4][x4--][y4]=k;
		}
		x1++;y1=1;
		y2--;x2=1;
		x3--;y3=m;
		y4++;x4=m;
	}
	int l,r,mid;
	l=0,r=maxn-minn;
	while(l<r)
	{
		mid=(l+r)>>1;
		if(check(mid))  r=mid;
		else  l=mid+1;
	}
	printf("%d",l);
//	fclose(stdin);
//	fclose(stdout);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/forever_dreams/article/details/82927612