二分图最大匹配 - 匈牙利算法

    技术2022-05-20  46

    问题描述:

     X集合(编号1~m),Y集合(编号m+1~n)。n,m<100. 给出若干组合(x, y)(相当于映射x->y),问最都能同时有几个组合(分配)。

     

    分析:

    题目可能简化描述得不太好,这显然是一个二分图最大匹配的问题。(二分图)

    可以用匈牙利算法,或者网络流来求解。这里介绍匈牙利算法。

    匈牙利算法的核心就是找增广路。不断找增广路,然后更新现有匹配M(M可以看成是X->Y的一个特殊的映射关系),每次更新,都能使匹配数增加一。当找不到增广路时,M就是最大的匹配。

     

    算法具体流程:

     

    ①记X上点为u,Y上点为v。记f[v] 为u->v的映射对应的u,即v的对应点,f[v]=u。(初始清零)记vis[u]为本次寻找增广路,点u是否访问过。

    dfs(u)表示当前从点u出发有没有增广路,若有返回1,否则为0.

    ②遍历X集合,(u=1~m),每次遍历vis[]清零,dfs(u),若有找到增广路(即dfs(u)=1),则匹配数加1。最后的匹配数即为最大。

    ③dfs中, 从u出发找v(邻接表存图),

    I.若v本次未访问过(!!),即vis[v]=0。

    1)若v为未盖点,即f[v]=0(尚无对应点),(说明增广路找到了,是以v为结尾的)。

    2)若dfs(f[v])=1,即f[v]≠0,v是盖点,f[v]是v在当前匹配M中对应的u' (联系增广路的定义,虚实交错)。则dfs(u')=1,同样说明增广路找到。v在这条增广路中。

    所以以上两个条件是并列的,或( || )关系。

    于是对M和这条增广路进行取反。令f[v]=u,(更新了M),return 1;(找到,回溯).

    3)没找到,遍历u的下一个邻接点v。

    II.若vis!=0; 不处理。

     

    其实这个算法我还是有些地方不是很明白。

    代码:

     #include<cstdio> #include<cstring> #include<vector> using namespace std; inline int Rint() {int x; scanf("%d", &x); return x;} #define MAXN 110 vector<int> G[MAXN]; int vis[MAXN]; int flag[MAXN]; //0-未盖 / 对应点-已盖 int n, m; int dfs(int u) { for(int i=0; i<(int)G[u].size(); i++) { int v = G[u][i]; if(!vis[v]) //未访问 { vis[v] = 1; if(!flag[v] || dfs(flag[v])) //!!不是dfs(v) -> dfs(flag[v]) 对应点 { flag[v] = u; return 1; } } } return 0; } int hungary() { memset(flag, 0, sizeof(flag)); int ans=0; // for(int i=1; i<=n; i++) //n??每个点 for(int i=1; i<=m; i++) //集合X中点 1~m { memset(vis, 0, sizeof(vis)); if(dfs(i)) ans++; } return ans; } void read_graph() { while(1) { int u = Rint(), v = Rint(); if(u == -1) break; G[u].push_back(v); //找的过程中,永远是从X集合出发的。u、flag[v]映射回u'... //G[v].push_back(u); } } void print_ans() //输出匹配M { for(int i=m+1; i<=n; i++) //flag[v]=u...v=m+1~n if(flag[i]) //v为盖点,输出对应的匹配边 { printf("%d->%d/n", flag[i], i); } } int main() { m = Rint(); n = Rint(); read_graph(); printf("%d/n", hungary()); print_ans(); }

     


    最新回复(0)