题目
题目描述
这是一个简单的游戏,在一个n*n的矩阵中,找n个数使得这n个数都在不同的行和列里并且要求这n个数中的最大值和最小值的差值最小。
输入
输入一个整数T表示T组数据。
对于每组数据第一行输入一个正整数n(1<=n<=100)表示矩阵的大小。
接着输入n行,每行n个数x(0<=x<=100)。
输出
对于每组数据输出一个数表示最小差值。
Sample Input
1
4
1 1 1 1
2 2 2 2
3 3 3 3
4 4 4 4
Sample Output
3
思路:
第一眼以为是搜索,没想到能用二分图匹配思想做出orz
因为是不同行不同列,可以分别将行和列作为二分图的两部分,然后二分差值,易知 0 <= 差值 <= max-min ,所以二分的 left=0,right=max-min,然后枚举在min,min+差值的范围中能否使每行都找到匹配(匈牙利算法),能就再缩小答案
代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e2+10;
#define faster ios::sync_with_stdio(false);cin.tie(0),cout.tie(0)
int link[maxn][maxn],use[maxn],ptn[maxn];
int n,mid,p;
bool find(int x){
for (int i = 1; i <= n; ++i) {
if (link[x][i]>=p&&link[x][i]<=p+mid&&!use[i]){
use[i]=1;
if (ptn[i]==-1|| find(ptn[i])){
ptn[i]=x;
return true;
}
}
}
return false;
}
bool match(){
memset(ptn,-1,sizeof ptn);
for (int i = 1; i <= n; ++i) {
memset(use,0,sizeof use);
if (!find(i)) return false;
}
return true;
}
signed main(){
faster;
int T;
scanf("%d",&T);
while (T--){
scanf("%d",&n);
int mi=105,ma=-5;
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= n ; ++j) {
scanf("%d",&link[i][j]);
ma=link[i][j]>ma?link[i][j]:ma;
mi=link[i][j]<mi?link[i][j]:mi;
}
}
int r=ma-mi,l=0;
while (r>=l){
int flag=0;
mid=(r+l)>>1;
for (p = mi; p+mid <= ma; ++p) {
if(match()){
flag=1;
break;
}
}
if(flag) r=mid-1;
else l=mid+1;
}
printf("%d\n",l);
}
return 0;
}