此题参考了
西瓜不懂柠檬的酸 dalao的博客
点击打开链接
/**
中文题,自己好好理解一下题意。
想到基本算法很简单,就是一个匈牙利算法,但是,难点在于,如何使得最大值,最小值的差最小
要求最大值与最小值的差值最小,是通过枚举边的下限和上限来完成
只需要用二分找一个区间,然后不断枚举这个区间是否可以达到最大匹配,一直二分到答案为止。*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int Maxx=105;
const int inf=0x3f3f3f3f;
int link[Maxx][Maxx],match[Maxx],vis[Maxx];
int MaxR,MinL,Max,Min,R,L,n;///MaxR,MinL记录最大最小值,Max,Min记录区间的上下限,L,R记录二分查找差值的上下限//
bool dfs(int x)
{
for(int i=1;i<=n;i++){
if(!vis[i]&&link[x][i]>=Min&&link[x][i]<=Max){
vis[i]=1;
if(!match[i]||dfs(match[i])){
match[i]=x;
return 1;
}
}
}
return 0;
}
bool hungary()///匈牙利算法
{
memset(match,0,sizeof match);
for(int i=1;i<=n;i++){
memset(vis,0,sizeof vis);
if(!dfs(i))
return 0;
}
return 1;
}
int main()
{
int t;
scanf("%d",&t);
while(t--){
memset(link,0,sizeof link);
MaxR=-1,MinL=inf;
scanf("%d",&n);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++){
scanf("%d",&link[i][j]);
MaxR=max(MaxR,link[i][j]);
MinL=min(MinL,link[i][j]);
}
L=0;
R=MaxR-MinL;///差值的的上下限
int ans=0;
while(L<=R){
int flag=0;
int mid=(L+R)/2;
for(int i=MinL;i+mid<=MaxR;i++){
Min=i;///如果差值为mid,那么,查找一个区间是,这个区间的最小值为i,最大值为i+mid
Max=i+mid;
if(hungary()){///如果在值在区间i,i+mid之间,并且可以完成匹配,那么说明可能存在一个差值比mid还要小,所以令上界减一,否则令下界加一
flag=1;
break;
}
}
if(flag){///上界减一,使得差值变小
ans=mid;
R=mid-1;
}
else///下界加一,使得差值变大
L=mid+1;
}
printf("%d\n",ans);
}
return 0;
}