在不同的行和列里,是将行和列看做成连个没有交集的集合,这里可以看成是二分图的匹配
要求是将最大值与最小值的差值最小化,那就是通过二分进行枚举,不断地缩减它的上界与下界
#include <iostream>
#include <string>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include<cmath>
using namespace std;
#define rep(i,a,b) for(int i=a;i<=b;i++)
const int INF=0x3f3f3f3f;
int n;
bool e[110][110];
int val[110][110];
int link[110];
bool vis[110];
int ans;
bool dfs(int x)
{
rep(i,1,n)
{
if(!vis[i]&&e[x][i])
{
vis[i]=1;
if((link[i]==0)||(dfs(link[i])))
{
link[i]=x;return true;
}
}
}
return false;
}
void add()
{
ans=0;
memset(link,0,sizeof link);
rep(i,1,n)//i可以看成行集合
{
memset(vis,0,sizeof vis);
if(!dfs(i)) return;
ans++;
}
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int minn=INF;
int maxx=-INF;
memset(val,0,sizeof val);
scanf("%d",&n);
rep(i,1,n)
rep(j,1,n)
scanf("%d",&val[i][j]),minn=min(minn,val[i][j]),maxx=max(maxx,val[i][j]);
int l=0;//下界一定要从0开始否则就会WA
int r=maxx-minn;//上届可以是最大值,但是在n*n的矩阵里最大值与最小值的差值最大为这个
int res;
memset(e,0,sizeof e);
while(l<=r)//二分枚举
{
int mid=(l+r)>>1;
for(int k=minn;k+mid<=maxx;k++){//从最小的值开始
rep(i,1,n)
rep(j,1,n)//这里的e数组是保存满足当前差值为mid的元素在哪个位置
if(val[i][j]<=k+mid&&val[i][j]>=k)
e[i][j]=1;
else e[i][j]=0;
add();//匈牙利模板
if(ans==n) break;//如果当前的mid(最小的差值)满足行与列的完美匹配,跳出
}
if(ans==n) res=mid,r=mid-1;//说明当前的差值可能大一些,那就将其减小,继续二分
else l=mid+1;
}
printf("%d\n",res);
}
}