二分圖最大匹配算法

/*
該模板不用建立超級源點和超級匯點,
直接把二分圖中對應的邊連接即可
*/
int V;//頂點數
vector<int> G[N];//圖的鄰接表表示
int match[N];//所匹配的點頂點
bool used[N];//dfs中用到的訪問標誌
//向圖中增加一條連接u和v的邊
void addedge(int u,int v)
{
    G[u].push_back(v);
    G[v].push_back(u);
}
//通過dfs尋找增廣路徑
bool dfs(int v)
{
    used[v]=true;
    for(int i=0;i<G[v].size();i++)
    {
        int u=G[v][i];
        int w=match[u];
        if(w<0||!used[w]&&dfs(w))
        {
            match[v]=u;
            match[u]=v;
            return true;
        }
    }
    return false;
}
//求解二分圖的最大匹配
int bipartite_matching()
{
    int ans=0;
    memset(match,-1,sizeof(match));
    for(int v=0;v<V;v++)
    {
        if(match[v]<0)
        {
            memset(used,0,sizeof(used));
            if(dfs(v))
                ans++;
        }
    }
    return ans;
}
/*
該模板是基於最大流算法的,
因此需要建立超級源點和超級匯點
*/
struct edge
{
    int to,cap,rev;//終點,容量,反向邊
};
vector<edge>G[N];//圖的鄰接矩陣表示
bool used[N];//dfs中用到的訪問標記
int can[N][N];//存原始圖,c[i][j]=1表示i到j可達
int n,m;//左點集的個數和右點集的個數
int s,t;//超級源點和超級匯點
void addedge(int u,int v,int w)
{
    G[u].push_back((edge){v,w,G[v].size()});
    G[v].push_back((edge){u,0,G[u].size()-1});
}
int dfs(int v,int t,int f)
{
    if(v==t)
        return f;
    used[v]=true;
    for(int i=0;i<G[v].size();i++)
    {
        edge &e=G[v][i];
        if(!used[e.to]&&e.cap>0)
        {
            int d=dfs(e.to,t,min(f,e.cap));
            if(d>0)
            {
                e.cap-=d;
                G[e.to][e.rev].cap+=d;
                return d;
            }
        }
    }
    return 0;
}
//求解從s到t上的最大流
int max_flow(int s,int t)
{
    int flow=0;
    for(;;)
    {
        memset(used,0,sizeof(used));
        int f=dfs(s,t,INF);
        if(f==0)
            return flow;
        flow+=f;
    }
}
//建圖
void Get_map()
{
    s=0,t=m+1;//按實際情況確定s和t
    //超級源點向左點集連邊
    for(int i=1;i<=n;i++)
        addedge(s,i,1);
    //右點集向超級匯點連邊
    for(int i=n+1;i<=m;i++)
        addedge(i,t,1);
    //左點集向右點集連邊
    for(int i=1;i<=m;i++)
    {
        for(int j=1;j<=m;j++)
            if(can[i][j])
               addedge(i,j,1);
    }
}

有向無環圖(DAG)的最小路徑覆蓋
有向無環圖中,路徑覆蓋就是在圖中找一些路徑,使之覆蓋了圖中的所有頂點,且任何一個頂點有且只有一條路徑與之關聯(如果把這些路徑中的每條路徑從它的起始點走到它的終點,那麼恰好可以經過圖中的每個頂點一次且僅一次)。最小路徑覆蓋就是找出最小的路徑條數,使之成爲原圖的一個路徑覆蓋。
在《圖論及應用》中有這樣一張圖:
這裏寫圖片描述
由圖中可以看出,我們主要把有向無環圖中每個頂點都拆成兩個,然後在連上對應的邊,那麼所求問題就可以轉化爲:
最小路徑覆蓋=(原圖)頂點數-對應的二分圖的最大匹配數
把圖轉化後,套用二分圖匹配模板即可求解(注意:轉化後頂點數要乘2,求解最終答案時要除2)

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