【题目】
题目描述:
有 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 区域和 B 区域的极差,其中极差较大的一个区域最小值是多少
提示:极差就是区域内最大值减去最小值。
输入格式:
第一行两个正整数 N,M
接下来 N 行,每行 M 个自然数 表示权值
输出格式:
输出一行表示答案
样例数据:
输入
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 ≤ ≤
【分析】
看到“极差较大的一个区域最小值是多少”这样的字眼的时候其实就知道这道题多半是二分
但是想到二分之后,check 中间值貌似又无从下手
于是在考场上打了个 20 分暴力就没去管它了
话不多说,开始讲正解,正解做法也就是二分,不过这个 check 比较巧妙
很容易想到的是,对于一个合法的分法,A 和 B 应该是呈阶梯状
设 A 分布在左上角,将矩阵旋转三次就可以得到所有的情况,因此写一个 check 函数就行
设最大值在 A 区域,那么我们从第一行开始找到第一个与最大值差值不在大于 的位置
在第二行不超过 的位置同样找一个这样的边界, 以下的所有行都这样弄
这样子我们就将矩阵划分为了两个区域,我们枚举出来的左边区域一定满足条件,那么我们再判断右区域满不满足(与最小值的差值在条件范围内,也即都小于等于 )
把旋转的四种情况都判断一下就可以得出答案
【代码】
#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;
}