无题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; }
$Never\ Give\ Up$