HDU 2236 无题II 二分+最大匹配

题目:http://acm.hdu.edu.cn/showproblem.php?pid=2236

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

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

Output
对于每组数据输出一个数表示最小差值。
在KM算法专题里面碰到的这道题,分到里面不太合适,应该分到匈牙利算法专题里面,被误导了。。。

想了好几天没有头绪,于是百度了一下, 看看大神们的写法,二分真是个神奇的东西,怪我理解不够深。。。

找出n*n个元素的最大值vmax和最小值vmin,那么差值一定在l=0到r=(vmax-vmin)之间,于是二分枚举差值进行匹配,在当前差值下能够完全匹配,那么更新r,同时保存当前差值,否则更新l。二分查到完毕后,输出之前保存的差值,就是结果

#include <iostream>
#include <string>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cctype>
#include <vector>
#include <map>
using namespace std;

const int N = 110;
const int INF = 0x3f3f3f3f;
int match[N];
int mpa[N][N];
int p, mid, n;
bool used[N];

bool dfs(int v)
{
    for(int i = 0; i < n; i++)
        if(mpa[v][i] >= p && mpa[v][i] <= p + mid && used[i] == false)//匹配条件要稍作修改,不是简单匈牙利模板
        {
            used[i] = true;
            if(match[i] == -1 || dfs(match[i]))
            {
                match[i] = v;
                return true;
            }
        }
    return false;
}

bool hungary()
{
    memset(match, -1, sizeof match);
    for(int i = 0; i < n; i++)
    {
        memset(used, 0, sizeof used);
        if(! dfs(i)) return false;
    }

    return true;
}

int main()
{
    int t;

    scanf("%d", &t);
    while(t--)
    {
        scanf("%d", &n);

        int vmax = -INF, vmin = INF;
        for(int i = 0; i < n; i++)
            for(int j = 0; j < n; j++)
            {
                scanf("%d", &mpa[i][j]);
                vmax = max(vmax, mpa[i][j]);
                vmin = min(vmin, mpa[i][j]);
            }

        int r = vmax - vmin, l = 0, res;
        while(l <= r) //发现大神们的二分姿势好奇怪,这个姿势是我的
        {
            mid = (l + r) >> 1;
            bool f = false;
            for(p = vmin; p + mid <= vmax; p++)
                if(hungary())
                {
                    f = true;
                    break;
                }

            if(f) res = mid, r = mid - 1;
            else l = mid + 1;
        }
        printf("%d\n", res);
    }

    return 0;
}

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值