这题我哪会啊。。。让最大值最小,很显然的想到了二分答案。怎么解决是否合法呢?其实合法的情况,如果左上角为红色的话,就是每行的红色的个数是单调的即可。我们还贪心的想,应该让最大值和最小值不在一块内。因此我们假定最大值mx在红色块内,最小值mn在蓝色块内,这次二分判定的答案为x。假设红色块在左上角,则我们逐行贪心的选择尽量多的合法的(即>=mx-x的),这样留给蓝色的不合法情况一定不会更多。为了让分块合法,我们还得保证每行选取的红色个数是单调不增的。贪心的选取完红色,看蓝色是否满足即可。因为红色快还可能在右上角,左下角,左上角,因此我们要把棋盘旋转4次,每次都从左上角开始做即可。复杂度O(nmlog1e9)注意优化一下常数。。不然可能过不去。。比如提前把四种旋转的情况都处理出来。。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
#define N 2010
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x*f;
}
int n,m,a[4][N][N],tmp[N][N],mx=0,mn=inf,now=0;
void rotate(int x,int y){
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
a[y][j][n-i+1]=a[x][i][j];
}
bool check(int x){
int last=m;
for(int i=1;i<=n;++i){
for(int j=1;j<=last;++j)
if(a[now][i][j]<mx-x){last=j-1;break;}
for(int j=last+1;j<=m;++j)
if(a[now][i][j]>mn+x) return 0;
}return 1;
}
bool jud(int x){
if(check(x)) return 1;now++;now&=3;swap(n,m);
if(check(x)) return 1;now++;now&=3;swap(n,m);
if(check(x)) return 1;now++;now&=3;swap(n,m);
if(check(x)) return 1;return 0;
}
int main(){
// freopen("sample3.in","r",stdin);
n=read();m=read();
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j) a[now][i][j]=read(),mx=max(mx,a[now][i][j]),mn=min(mn,a[now][i][j]);
for(int i=1;i<=3;++i){
rotate(now,now+1);now++;swap(n,m);
}now=0;swap(n,m);
int l=0,r=mx-mn;
while(l<=r){
int mid=l+r>>1;
if(jud(mid)) r=mid-1;else l=mid+1;
}printf("%d\n",r+1);
return 0;
}