POJ3041 二分圖 最小點覆蓋=最大匹配數

題意:有一個N*N的網格,該網格有K個障礙物.你有一把武器,每次你使用武器可以清楚該網格特定行或列的所有障礙.問你最少需要使用多少次武器能清除網格的所有障礙物?

分析:把網格的行看作左邊點集的點,網格的列看成右邊點集的點. 如果(i,j)格有障礙,那麼就從左邊i點到右邊j點之間連接一條邊,這樣問題就轉化爲了求新圖的最小點覆蓋即最少選多少個點能夠覆蓋所有的邊

利用二分圖的特性最小點覆蓋=最大匹配數,求新圖最大匹配數即可。

匈牙利算法解法:

#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
#include<vector>
using namespace std;
const int maxn=505;
vector<int> g[maxn];
bool v[maxn];
int from[maxn];
bool find(int x)
{
    for(int i=0;i<g[x].size();i++)
    {
        if(!v[g[x][i]])
        {
            v[g[x][i]]=true;
            if(!from[g[x][i]]||find(from[g[x][i]]))
            {
                from[g[x][i]]=x;
                return true;
            }
        }
    }
    return false;
}
int solve(int n)
{
    int res=0;
    for(int i=1;i<=n;i++)
    {
        memset(v,0,sizeof v);
        if(find(i))
            res++;
    }
    return res;
}
int main()
{
    ios_base::sync_with_stdio(0);
    int n,m;
    cin>>n>>m;
    int x,y;
    for(int i=1;i<=m;i++)
    {
        cin>>x>>y;
        g[x].push_back(y);
    }
    cout<<solve(n)<<endl;
    return 0;
}

 

 

Hopcroft-Karp算法解法:

 

dx[ ],dy[ ]分別表示二分圖左右部頂點的距離標號

mx[ ],my[ ]分別表示二分圖左右部頂點的匹配節點

#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
#include<vector>
#include<queue>
using namespace std;
const int maxn=505;
vector<int> g[maxn];
bool v[maxn];
int dx[maxn],dy[maxn];
int mx[maxn],my[maxn];
bool find(int x)
{
    for(int i=0;i<g[x].size();i++)
    {
        if(!v[g[x][i]]&&dy[g[x][i]]==dx[x]+1)
        {
            v[g[x][i]]=true;
            if(!my[g[x][i]]||find(my[g[x][i]]))
            {
                mx[x]=g[x][i];
                my[g[x][i]]=x;
                return true;
            }
        }
    }
    return false;
}
int solve(int n)
{
    int res=0;
    while(true)
    {
        bool flag=false;
        queue<int> q;
        memset(dx,0,sizeof dx);
        memset(dy,0,sizeof dy);
        for(int i=1;i<=n;i++)
        {
            if(!mx[i])
                q.push(i);
        }
        while(!q.empty())
        {
            int now=q.front();q.pop();
            for(int i=0;i<g[now].size();i++)
            {
                if(!dy[g[now][i]])
                {
                    dy[g[now][i]]=dx[now]+1;
                    if(my[g[now][i]])
                    {
                        dx[my[g[now][i]]]=dy[g[now][i]]+1;
                        q.push(my[g[now][i]]);
                    }
                    else flag=1;
                }
            }
        }
        if(!flag) break;
        memset(v,0,sizeof v);
        for(int i=1;i<=n;i++)
        {
            if(!mx[i]&&find(i))
                res++;
        }
    }
    return res;
}
int main()
{
    ios_base::sync_with_stdio(0);
    int n,m;
    cin>>n>>m;
    int x,y;
    for(int i=1;i<=m;i++)
    {
        cin>>x>>y;
        g[x].push_back(y);
    }
    cout<<solve(n)<<endl;
    return 0;
}

 

兩者相對比,匈牙利算法實現簡單,是最經典的二分圖匹配算法,而Hopcroft-Karp算法是對匈牙利算法的優化。利用匈牙利算法一次只能找到一條增廣路徑,而Hopcroft-Karp算法一次能找到多條不相交的增廣路徑,然後根據這些增廣路徑添加多個匹配。相當於批量處理。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章