无题II HDU - 2236 【二分图+二分答案】

题目

这是一个简单的游戏,在一个n*n的矩阵中,找n个数使得这n个数都在不同的行和列里并且要求这n个数中的最大值和最小值的差值最小。

Input

输入一个整数T表示T组数据。 对于每组数据第一行输入一个正整数n(1<=n<=100)表示矩阵的大小。 接着输入n行,每行n个数x(0<=x<=100)。

Output

对于每组数据输出一个数表示最小差值。

Sample Input

1
4
1 1 1 1
2 2 2 2
3 3 3 3
4 4 4 4

Sample Output

3

分析

首先就可以想到,如果对于找出来的n个数,刚好属于某个区间[l,r],那么显然可以得到答案就是r-l。所以我们直枚举所有可能的区间,然后二分图匹配,让所有可能的匹配权值都属于这个区间,如果成立,那么这个区间是可行的。

然后就可以想到,对于答案来说是要找一个区间长度最小的区间,然后我们就二分一下,然后枚举所有长度为当前答案的区间,然后判断当前区间是否可行,如果当前区间可行,就说明当前答案正确,更新一下就完事了。

代码

#include <bits/stdc++.h>
const int inf = 0x3f3f3f3f;
using namespace std;
const int maxn = 101;
const int maxm = 10103;
int umaxn, vmaxn;
int a[maxn][maxn];
bool us[maxn];
int li[maxn];
bool dfs(int u, int ll, int rr) {
    for(int v=1; v<=umaxn; v++) {
        if(a[u][v] >=ll && a[u][v]<=rr && !us[v]) {
            us[v] = true;
            if(li[v] == -1 || dfs(li[v], ll, rr)) {
                li[v] = u;
                return true;
            }
        }
    }
    return false;
}
bool ts(int mid, int ll , int rr) {
    int res = 0;
    memset(li,-1,sizeof(li));
    for(int u=1; u<=umaxn; u++) {
        memset(us,0,sizeof(us));
        if(!dfs(u, ll , rr)) return false;
    }
    return true;
}
bool check(int mid){
    for(int i=0;i+mid<=100;i++){
        if(ts(mid, i, i+mid)){
            return true;
        }
    }
    return false;
}
int main(){
    int T;
    scanf("%d", &T);
    while(T--){
        int n;
        scanf("%d", &n);
        umaxn = vmaxn = n;
        int Min = inf, Max = -1;
        for(int i=1; i<=n; i++)
            for(int j=1; j<=n; j++){
                scanf("%d",&a[i][j]);
                Min = min(Min, a[i][j]);
                Max = max(Max, a[i][j]);
            }
        int l = 0, r = Max - Min;
        while(l<=r) {
            int mid = (l+r)>>1;
            if(check(mid)){
                r = mid-1;
            } else l = mid+1;
        }
        printf("%d\n", l);
    }
    return 0;
}

 

posted @ 2020-05-13 15:54  Vocanda  阅读(159)  评论(0编辑  收藏  举报