2M——二分图+二分答案

题目

这是一个简单的游戏,在一个\(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

题解

解题思路

我们看题目,每行每列都找一个数,可以想到用二分图最大匹配
然后就是枚举了,暴力的话一定是不行的
我们就可以通过二分答案来进行枚举

代码

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 105, M = 0x7fffffff;
int T, n, a[N][N], l, r, mid, ans, mi, ma, m[N], v[N];
bool dfs(int x, int s, int d) {
    for(int i = 1; i <= n; i++) 
        if (!v[i] && s <= a[x][i] && s + d >= a[x][i]) {
            v[i] = 1;
            if (m[i] == -1 || dfs(m[i], s, d)) {
                m[i] = x;
                return 1;
            }
        }
    return 0;
}
bool judge(int d) {
    for(int s = mi; s + d <= ma; s++) {
        memset(m, -1, sizeof(m));
        int sum = 0;
        for(int i = 1; i <= n; i++) {
            memset(v, 0, sizeof(v));
            if (dfs(i, s, d)) sum++;
        }
        if (sum == n) return 1;
    }
    return 0;
}
int main() {
    scanf("%d", &T);
    while (T--) {
        scanf("%d", &n);
        mi = M; ma = -M;
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= n; j++) {
                scanf("%d", &a[i][j]);
                mi = min(mi, a[i][j]);
                ma = max(ma, a[i][j]);
            }
        l = 0; r = ma - mi; ans = 0;
        while(l <= r) {
            mid = (l + r) >> 1;
            if (judge(mid)) ans = mid, r = mid - 1;
            else l = mid + 1;
        }
        printf("%d\n", ans);
    }
    return 0;
}
posted @ 2020-05-13 20:22  Shawk  阅读(97)  评论(0编辑  收藏  举报