一、题目
二、解法
考虑二分答案,我们把最小值强加于
JOI
\text{JOI}
JOI,最大值强加于
IOI
\text{IOI}
IOI。
考虑怎样检查,先想一想势力范围应该如何划分。
假设蓝色是
JOI
\text{JOI}
JOI的势力范围,可以发现如果一列一列地区填的话,能填的行应该是单调递减的,既然我们已经二分了答案,为了减轻对面的负担,
JOI
\text{JOI}
JOI肯定拿的越多越好,所以我们就能够
O
(
n
2
)
O(n^2)
O(n2)模拟这个过程,然后检查对面是否合法,注意我们要从每个角落开始跑,一共四遍。
还有一个问题,如果
JOI
\text{JOI}
JOI拿不到最小值怎么办,
JOI
\text{JOI}
JOI心中会想:没关系,反正我已经尽力了,剩下的交给对面就行了,我肯定是合法的。
#include <cstdio>
#include <iostream>
#define inf 0x3f3f3f3f
using namespace std;
const int MAXN = 2005;
int read()
{
int x=0,flag=1;char c;
while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x*flag;
}
int n,m,ans,cur,Min=inf,Max,a[MAXN][MAXN];
bool vis[MAXN][MAXN];
void clear()
{
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
vis[i][j]=0;
}
bool fuck(int x)
{
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(Max-a[i][j]>x && !vis[i][j])
return 0;
return 1;
}
bool check(int x)
{
//左上角
clear();cur=n;
for(int j=1;j<=m;j++)
{
for(int i=1;i<=cur;i++)
{
if(a[i][j]-Min>x)
{
cur=i-1;
break;
}
vis[i][j]=1;
}
}
if(fuck(x)) return 1;
//左下角
clear();cur=1;
for(int j=1;j<=m;j++)
{
for(int i=n;i>=cur;i--)
{
if(a[i][j]-Min>x)
{
cur=i+1;
break;
}
vis[i][j]=1;
}
}
if(fuck(x)) return 1;
//右上角
clear();cur=n;
for(int j=m;j>=1;j--)
{
for(int i=1;i<=cur;i++)
{
if(a[i][j]-Min>x)
{
cur=i-1;
break;
}
vis[i][j]=1;
}
}
if(fuck(x)) return 1;
//右下角
clear();cur=1;
for(int j=m;j>=1;j--)
{
for(int i=n;i>=cur;i--)
{
if(a[i][j]-Min>x)
{
cur=i+1;
break;
}
vis[i][j]=1;
}
}
if(fuck(x)) return 1;
return 0;
}
void dich(int l,int r)
{
if(l>r) return ;
int mid=(l+r)>>1;
if(check(mid))
{
ans=mid;
dich(l,mid-1);
}
else
dich(mid+1,r);
}
int main()
{
n=read();m=read();
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
a[i][j]=read();
Min=min(Min,a[i][j]);
Max=max(Max,a[i][j]);
}
ans=Max-Min;
dich(0,Max-Min);
printf("%d\n",ans);
}