NOIP模拟 矩阵分组(二分答案)

【题目描述】

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

要求:

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

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

如:

其中(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-7

n<=200,m<=200

8-10

n<=2000,m<=2000

所有权值1<=a_ij<=10^9

【题目分析】

看到题目描述的“极值的最小值”,想到了二分答案,但不知道怎么写check函数,20分滚粗。。。

首先推个结论:所选区域构成图形一定是阶梯状,否则就会出现拐两次弯或者不相通的情况。

对于每个二分的答案,因为矩阵可以旋转,所以需检查四次,对于每次检查,可以一列一列的扫,每次记录当前行极值与二分的值比较,如果超过就断开,记录每一列的个数看是否递增或者递减(二者取一,因为会旋转)。

【代码~】

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL MAXN=2001;
const LL INF=0x3f3f3f3f;

LL n,m;
LL ma[4][MAXN][MAXN];
LL gmax=-INF,gmin=INF;
LL endi[MAXN];

bool pd(LL x,LL u)
{
	if(u&1)
	  swap(n,m);
	endi[0]=m;
	LL j;
	for(LL i=1;i<=n;++i)
	{
	    for(j=1;j<=endi[i-1];++j)
	    {
	  	    if(gmax-ma[u][i][j]>x)
	  	      break;
	    }
	    endi[i]=j-1;
	}
	for(LL i=1;i<=n;++i)
	{
	    for(LL j=endi[i]+1;j<=m;++j)
	    {
	    	if(ma[u][i][j]-gmin>x)
	    	{
	    		if(u&1)
	    		  swap(n,m);
	    	    return false;
	    	}
	    }
	}
	if(u&1)
	  swap(n,m);
	return true;
}

bool check(LL x)
{
	if(pd(x,0))
	  return true;
	if(pd(x,1))
	  return true;
	if(pd(x,2))
	  return true;
	if(pd(x,3))
	  return true;
	return false;
}

LL Read()
{
	LL i=0,f=1;
	char c;
	for(c=getchar();(c>'9'||c<'0')&&c!='-';c=getchar());
	if(c=='-')
	  f=-1,c=getchar();
	for(;c>='0'&&c<='9';c=getchar())
	  i=(i<<3)+(i<<1)+c-'0';
	return i*f;
}

int main()
{
	n=Read(),m=Read();
	LL x=1,x1=1,x2=n,x3=m,y=1,y1=n,y2=m,y3=1,t;
	for(LL i=1;i<=n;++i)
	{
		  for(LL j=1;j<=m;++j)
		  {
		      t=ma[0][x][y++]=ma[1][x1++][y1]=ma[2][x2][y2--]=ma[3][x3--][y3]=Read();  
		      if(t>gmax)
		        gmax=t;
		      if(t<gmin)
		        gmin=t;
		      
		  }
		  x++,y=1;
		  y1--,x1=1;
		  x2--,y2=m;
		  y3++,x3=m;
	}
	LL l=0,r=gmax-gmin;
	while(l<r)
	{
		LL mid=l+r>>1;
		if(check(mid))
		  r=mid;
		else
		  l=mid+1;
	}
	cout<<l<<endl;
	return 0;
}

 

posted @ 2018-10-03 16:16  Ishtar~  阅读(196)  评论(0编辑  收藏  举报