Tony's Log

Algorithms, Distributed System, Machine Learning

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

Just for study from its editorial~

Lesson learnt: an optimized Hungarian Algorithm: Hopcroft-Karp Algorithm (a batched version of Hungarian)
A very good article on it (in Chinese): https://www.renfei.org/blog/bipartite-matching.html

The basic idea of Hungarian is: find all augment pathes and flip (matched\unmatched toggled) them, recursively.
And Hopcroft-Karp is a batched version of Hungarian: we simply check all edges at each node.

#include <cmath>
#include <cstdio>
#include <vector>
#include <queue>
#include <iostream>
#include <algorithm>
#include <unordered_map>
#include <unordered_set>
using namespace std;

#define MAX 2001

typedef long long LL;

int N, M, K;
vector<int> edges[MAX]; // edges of left side
vector<bool> visited(MAX);
vector<int> Left(MAX), Right(MAX);
vector<vector<LL>> dist(MAX, vector<LL>(MAX));

/*
 *    Hopcroft-Karp Algorithm: optimized(batched) Hungarian Algorithm
 *     Complexity: E*sqrt(V)
 */

//    True: augment path flipped
//    False:augment path stay the same
//
bool dfs(int u)
{
    if(visited[u]) return false;
    
    visited[u] = true;
    
    //    Flip input u-v pairs
    for(auto v : edges[u])
    {
        if(Right[v] == -1) // u-v not matched
        {
            // then match u-v
            Right[v] = u, Left[u] = v;
            return true; 
        }
    }
    
    //    Given all input u-v are matched    then match deeper pathes
    for(auto v : edges[u])
    {
        if(dfs(Right[v])) // flipped deeper?
        {
            //    then flip current edge too
            Right[v] = u, Left[u] = v;
            return true;
        }
    }
    return false;
}

int match()
{
    //    Cleanup work
    Left.assign(MAX, -1);    
    Right.assign(MAX, -1);
    
    int i, ret = 0;
    bool done = true;
    do
    {
        done = true;
        
        // for the new aug. path
        visited.assign(MAX, 0);
        
        for(int i = 1; i <= N; i ++)
            if(Left[i] == -1 && dfs(i))
                done = false; // augment-able? again..
        
    }while(!done);
    
    //    Count no. of matched edges
    for(int i = 1; i <= N; i ++) 
        ret += Left[i] != -1;
    return ret;
}
/**********************************************/

bool check(LL val)
{
    //    Pick reasonable edges
    for( int i=1 ; i<=N ; i++)    
    for( int j=1 ; j<=M ; j++)
        if(dist[i][j] <= val)
            edges[i].push_back(j);

    //    Run Hopcroft-Karp
    LL num_match = match();

    //    Clean for the next check
    for(int i= 1 ; i<= N ; i++)
        edges[i].clear();

    return num_match >= K;
}

int main()
{    
    cin >> N >> M >> K;
    
    //    Get input array
    vector<pair<LL, LL>> P(N + 1), Q(M + 1);
    for(int i = 1; i <= N; i ++)
        cin >> P[i].first >> P[i].second;
    for(int i = 1; i <= M; i ++)
        cin >> Q[i].first >> Q[i].second;
    
    //     Calculate distances    
    for(int i=1 ; i<=N ; i++)    
    for(int j=1 ; j<=M ; j++)
    {
        LL a = P[i].first - Q[j].first;
        LL b = P[i].second - Q[j].second;
        dist[i][j] = a * a + b * b;
    }

    //    Binay Search the min matching result for K
    LL low = 0, high = 10000000000000000;
    while(low < high)
    {
        LL mid = (low + high) >> 1;
        if(check(mid))    high = mid;
        else            low  = mid + 1;        
    }
    cout << low << endl;
    return 0;
}
View Code

 

posted on 2016-01-27 14:32  Tonix  阅读(327)  评论(0编辑  收藏  举报