算法:二分图的匹配 ———— 最小路径覆盖
最小路径覆盖=最小路径覆盖=|G|-最大匹配数
int nx,ny;//x集合和y集合中顶点的个数
int edge[maxn][maxn];//edge[i][j]为1表示ij可以匹配
int cx[maxn],cy[maxn];//用来记录x集合中匹配的y元素是哪个!
int visited[maxn];//用来记录该顶点是否被访问过!
int path(int u)
{
int v;
for(v=0;v<ny;v++)
{
if(edge[u][v]&&!visited[v])
{
visited[v]=1;
if(cy[v]==-1||path(cy[v]))//如果y集合中的v元素没有匹配或者是v已经匹配,但是从cy[v]中能够找到一条增广路
{
cx[u]=v;
cy[v]=u;
return 1;
}
}
}
return 0;
}
int maxmatch()
{
int res=0;
memset(cx,0xff,sizeof(cx));//初始值为-1表示两个集合中都没有匹配的元素!
memset(cy,0xff,sizeof(cy));
for(int i=0;i<=nx;i++)
{
if(cx[i]==-1)
{
memset(visited,0,sizeof(visitited));
res+=path(i);
}
}
return res;
}</span>
深度优先方法2:
/****************************************************
二分图匹配(匈牙利算法的DFS实现)
INIT:g[][]两边定点划分的情况
CALL:res=hungary();输出最大匹配数
优点:适于稠密图,DFS找增广路快,实现简洁易于理解
时间复杂度:O(VE);
****************************************************/
const int MAXN=1000;
int uN,vN; //u,v数目
int g[MAXN][MAXN];//编号是0~n-1的
int linker[MAXN];
bool used[MAXN];
bool dfs(int u)
{
int v;
for(v=0;v<vN;v++)
if(g[u][v]&&!used[v])
{
used[v]=true;
if(linker[v]==-1||dfs(linker[v]))
{
linker[v]=u;
return true;
}
}
return false;
}
int hungary()
{
int res=0;
int u;
memset(linker,-1,sizeof(linker));
for(u=0;u<uN;u++)
{
memset(used,0,sizeof(used));
if(dfs(u)) res++;
}
return res;
}
DFS就是利用增广路径的特性,为每个节点找到配对的点。核心是if (linker[v] == -1 || dfs(linker[v])),如果u可以和v配对,则直接返回;如果v已经有配对了,那么试试看能不能让v的对象另外再找个点配对,将v腾出来。然后一直递归下去。
广度优先遍历:
queue<int> Q;
//Q队列,用来进行广度优先遍历
int prev[__maxNodes];
int Hungarian()
{
int ans = 0;
memset(matching, -1, sizeof(matching));
memset(check, -1, sizeof(check));
for (int i=0; i<num_left; ++i)
{
if(matching[i] == -1)
{
while (!Q.empty())
Q.pop();
Q.push(i);
prev[i] = -1; // 设 i 为路径起点
bool flag = false; // 尚未找到增广路
while (!Q.empty() && !flag)
{
int u = Q.front();
for (iterator_t ix = G[u].begin(); ix != G[u].end() && !flag; ++ix)
{
int v = edges[*ix].to;
if (check[v] != i)
{
check[v] = i;
Q.push(matching[v]);
//这里将matching[v]入队,如果matching[v]是-1的话,会直接进入下面的else,即 flag会变成true,到时候循环直接退出,-1也会被弹出。
//当matching[v]有值时,表示这个节点可以出现在树的下一层,所有加入到层序遍历队列中
if (matching[v] >= 0)
//u可以和v配对,但是v已经有对象了,将u作为备胎记录下来
{ // 此点为匹配点
prev[matching[v]] = u;
//prev数组中存对应下标的点的备胎
}
else
{ // 找到未匹配点,交替路变为增广路
flag = true;
int d=u,e=v;
while(d != -1)
{
//u原来是和t配对的,但是现在u甩了t和v配对
//所有t去找备胎配对
//依次循环下去
int t = matching[d];
matching[d] = e;
matching[e] = d;
d = prev[d];
e = t;
}
}
}
}
Q.pop();
}
if (matching[i] != -1)
++ans;
}
}
return ans;
}
看每新加入一个节点能不能形成新的增广路径。方法就是,以新加入的节点为根节点,生成匈牙利树,看是否能找到未匹配的叶子结点。如果有就说明加入当前节点后可以形成一条增广路径,及配对数加一。