无题II HDU - 2236(匈牙利算法+二分)

此题参考了 西瓜不懂柠檬的酸  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;
}

猜你喜欢

转载自blog.csdn.net/qq_41874469/article/details/80148182