帶權二分圖最優匹配KM算法

KM

算法思想

設有A[],B[]二分圖,貪心的去找與A[i]的最大匹配,若沒有找到就增加邊,直到找到最優爲止;

實現步驟

1.初始化l[],r[],數組,設置頂標,即找到與A[i]最大匹配的B[],存在l[]數組中,r[]數組初始化爲0;
2.dfs()尋找增廣路徑(最大匹配),和匈牙利類似;
a.如果找到匹配點就進入下一匹配點;
b.如果沒有找到就增加新的路徑;
注意:
在實現最小匹配時,將兩點的權值變爲負值,初始化l[]數組時需要將其設爲-INF,其餘不變;

代碼

例題:HDU2255

#include <bits/stdc++.h>
using namespace std;
const int N=302;
const int inf=0x3f3f3f3f;
int mp[N][N],l[N],r[N],match[N],slack[N];//mp[]存邊,l[],r[]存改變後的期望值,match[]存相連的點,slack[]存最小期望值用於新建邊;
bool pl[N],pr[N];
int n;
bool dfs(int x)
{
    pl[x]=true;
    for(int i=1;i<=n;++i){
        if(pr[i]) continue;
        int t=l[x]+r[i]-mp[x][i];       //期望的差值,用於判定是否可以匹配和建新邊;
        if(!t){
            pr[i]=true;
            if(match[i]==-1||dfs(match[i])){
                match[i]=x;
                return true;
            }
        }
        else{
            slack[i]=min(slack[i],t);
        }
    }
    return false;
}
int KM()
{
    memset(match,-1,sizeof(match));
    memset(l,0,sizeof(l));
    memset(r,0,sizeof(r));
    for(int i=1;i<=n;++i){
        for(int j=1;j<=n;++j){
            l[i]=max(l[i],mp[i][j]); //初始化l[]數組;
        }
    }
    for(int i=1;i<=n;++i){
        memset(slack,inf,sizeof(slack)); //初始化slack,最小期望值;
        while(true){
            memset(pl,false,sizeof(pl));
            memset(pr,false,sizeof(pr));
            if(dfs(i)) break;
            int minn=inf;             //沒有找到匹配點,需要建立新邊;
            for(int j=1;j<=n;++j){
                if(!pr[j]) minn=min(minn,slack[j]);
            }
            for(int j=1;j<=n;++j){
                if(pl[j]) l[j]-=minn;
                if(pr[j]) r[j]+=minn;
                else slack[j]-=minn;
            }
        }
    }
    int ans=0;
    for(int i=1;i<=n;++i){
        ans+=mp[match[i]][i];
    }
    return ans;
}
int main()
{
    while(scanf("%d",&n)!=EOF){
        for(int i=1;i<=n;++i){
            for(int j=1;j<=n;++j){
                scanf("%d",&mp[i][j]);
            }
        }
        printf("%d\n",KM());
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章