二分图匹配(基础)

二分图:又叫二部图,图G中顶点集V可以分成互不相交的子集(X,Y),并且图中的每一条边所关联的点分别属于两个不同的顶点集,则图G叫二分图。(不含奇环)

二分图的匹配:给定一个二分图G的子图M,M的边集中任意两条边都不依附于同一个顶点(点单独配对),则称M是一个匹配。

二分图的最大匹配:边数最大的一个匹配就是二分图的最大匹配。

看上去二分图匹配好像没有什么用途,但以下三个定理会有大用处:

1.二分图的最小点覆盖(x或y中的都行) = 最大匹配;

2.二分图的最大独立集 = 顶点数 - 最大匹配;

(最大独立集是从图中找出最多的顶点数并且顶点两两之间没有边)

3.有向无环图的最小路径覆盖 = 顶点数- 最大匹配。


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

   算法的思路是不停的寻找增广路(交错轨),并增加匹配的个数;增广轨的形式:第一条边未参与匹配,第二条边参与匹配,第三条边未参与匹配,...,最后一条边未参与匹配,显然它有奇数条边。那么对于这样一条路径,可以将它变为:第一条边参与匹配,第二条边未参与匹配,第三条边参与匹配,...,最后一条边参与匹配。匹配依然合法,但匹配数增加了一对。可以证明,当找不到增广轨时,就得到了最大匹配,这就是匈牙利算法的思路。

const int N = 50;
const int M = 50;
bool use[M];		//记录y中节点是否使用
int link[M];		//记录当前与y节点相连的x的节点:i未加入匹配时为link[i]==0
bool g[N][M];		//记录连接x和y的边,如果i和j之间有边则为1,否则为0
int gn,gm;			//二分图中x和y中点的数目

bool find(int v)		//对x中的结点v,找增广路径。
{
    int i;
    for(i = 1; i <= gm; i++)	//对结点v发出的每条边
    {
       if(g[v][i] && !use[i])
       {
           use[i] = true;
//如果y中的结点i还没有加入到匹配中(link[i]==0),可直接连线;
  //或者y加入到了匹配中,根据link[i]找到x中的结点v,根据v发出的边寻找另外一个未加入匹配结点y
  //找到就记录到link中,返回true;找不到返回false(即找增广路径)。
           if(link[i] == 0 || find(link[i]))
           {
              link[i] = v;
              return true;
           }
       }
    }
    return false;
}

int MaxMatch()			//找二分图的最大匹配数。
{
    int i,ans=0;
    for(i = 1; i <= gn; i++)
    {
       memset(use,0,sizeof(use));
       if(find(i)) ans++;
    }
	return ans;
}

 

    最小路径覆盖

    在有向无环图G中,找出最小的路径条数,使之覆盖所有顶点,且任意一个顶点有且只有一条路径与之关联。从这可以看出:

1.一个单独的顶点是一条路径;
2.如果存在一条路径p1,p2,...,pk,其中p1为起点,pk为终点,那么在覆盖图中,p1,p2,...,pk这些顶点不再与其它的顶点之间存在有向边。
最小路径覆盖就是找出最小的路径条数,使之覆盖所有点。
最小路径覆盖数 = P - 最大匹配数。(G必须是无环的有向图)
其中最大匹配数的求法是把G中的每个顶点pi分成两个顶点pi与pi',如果G中存在一条pi到pj的边,那么在二分图G'中就有一条连接pi'与pj'的无向边;这里pi'就是G中pi的出边,pj'就是G中pj的一条入边。
可以这样理解:
匹配数为0,那么图G中不存在有向边,显然有最小覆盖数=P-0=P。
如果在G'中增加一条匹配边pi'-->pj',那么在图G中就有pi-->pj在一条路径上,于是覆盖的路径数减1,如此增加,直到二分图的最大匹配,也就求出了最小路径覆盖。

 

你可能感兴趣的:(算法,J#)