【NOIP校内模拟】矩阵分组 matrix
【描述】
有 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。没有别的分法,可以使答案更小。
很容易想到二分。
我们二分什么呢?二分极值
首先,A,B两部分一定是逐行长度递增(递减)的图形,才能满足最多拐一次弯到达,而且A,B可以互换
其次,最大值和最小值肯定不能在一个区域里,这样得出的答案肯定不是最优的,我们可以根据这个性质,找到check的方法
假设最大值在A这一部分,A覆盖了左上角,我们现在要先构造这么一个A,再来检验剩下B是否合法。
那么对于每一行,我们往右扫,直到找到一个x1 使得最大值x1>mid
那么对于剩下的B,如果有一个x1-最小值>mid,肯定不合法。
最后,A是可以在任意一个角的,这就需要将矩阵翻转,代码实现很简单。
#include<iostream>
#define N 2005
#define MAX 1000000005
using namespace std;
int n,m,maxv,minv=MAX;
int matrix[4][N][N]; //0 90 180 270
void INIT()
{
ios::sync_with_stdio(false);
cin.tie(NULL),cout.tie(NULL);
cin>>n>>m;
int x0=1,y0=1,x1=1,y1=n,x2=n,y2=m,x3=m,y3=1;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
int temp;
cin>>temp;
maxv=max(maxv,temp);
minv=min(minv,temp);
matrix[0][x0][y0]=matrix[1][x1][y1]=matrix[2][x2][y2]=matrix[3][x3][y3]=temp;
y0++,x1++,y2--,x3--;
}
x0++;y0=1;
x1=1;y1--;
x2--;y2=m;
x3=m;y3++;
}
}
int end[N];
bool BIGcheck(int state,int d)
{
if(state&1) swap(n,m);
//先构造包含最大值的区域 是一个逐行递减的图形
int i,j;
end[0]=m;
for(i=1;i<=n;i++)
{
for(j=1;j<=end[i-1];j++)
{
if(maxv-matrix[state][i][j]>d) break;
}
end[i]=j-1;
}
//检验包含最小值的区域
for(int i=1;i<=n;i++)
{
for(int j=end[i]+1;j<=m;j++)
{
if(matrix[state][i][j]-minv>d)
{
if(state&1) swap(n,m);//换回来
return false;
}
}
}
if(state&1) swap(n,m);
return true;
}
bool check(int d)
{
//四个角落
if(BIGcheck(0,d)) return true;
if(BIGcheck(1,d)) return true;
if(BIGcheck(2,d)) return true;
if(BIGcheck(3,d)) return true;
return false;
}
void SOLVE()
{
//二分较大的极值
int l=0,r=maxv-minv;
while(l<r)
{
int m=(l+r)>>1;
if(check(m)) r=m;
else l=m+1;
}
cout<<l;
}
int main()
{
INIT();
SOLVE();
return 0;
}