传送门
不能转两次弯说明什么?
把图分成两个部分其实就相当于画一条单调的线。
如下图
如果我们分蓝色的,以上面那一行为最大,那么下面的蓝色的终点是不可以超过上面那行的。
但是蓝色既可以左右延伸,也可以上下延伸。
可以从4个方向出发。
但是写4个分色有些麻烦。
我们可以用从左开始,向右延伸的方法,然后分别把图选择90,180,270.
就可以得到所有的情况了。
得到了图该怎样做?
二分答案。
我们首先得到最大值与最小值。
答案肯定小于最大值-最小值。
假定最大值在蓝色,最小值在红色。
二分一个答案,用最大值-元素>=ans为条件去构造蓝色块。
然后再去判断红色块中的元素是不是都满足条件即可。
#include <cstdio>
#include <iostream>
using namespace std;
const int inf=1e9;
int n,m,a[2010][2010],ans=inf,maxx=-inf,minx=inf;
void turn90()//对调
{
for(int i=1;i<=n;i++)
for(int j=1;j<=m/2;j++)
swap(a[i][j],a[i][m-j+1]);
}
void turnd()//选择
{
for(int i=1;i<=n/2;i++)
for(int j=1;j<=m;j++)
swap(a[i][j],a[n-i+1][j]);
}
bool check(int mid)
{
int p=m+1;
for(int i=1;i<=n;i++)
{
int t=0;
for(int j=1;j<=min(p,m);j++)
if(maxx-a[i][j]<=mid) t=max(t,j);//找i行的最大延伸
else break;
p=t;
for(int j=t+1;j<=m;j++)
if(a[i][j]-minx>mid) return 0;//满不满足条件
}
return 1;
}
int get_ans()
{
int l=0,r=maxx-minx+1;
while(l<=r)
{
int mid=(l+r)>>1;
if(check(mid)) r=mid-1;
else l=mid+1;
}
return l;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
scanf("%d",&a[i][j]),maxx=max(a[i][j],maxx),minx=min(a[i][j],minx);
ans=min(ans,get_ans());
turn90();
ans=min(ans,get_ans());
turnd();
ans=min(ans,get_ans());
turn90();
ans=min(ans,get_ans());//枚举4种情况
printf("%d\n",ans);
}