二分图判定和匹配问题(匈牙利算法和KM算法)

二分图定义:

二分图又称作二部图。设G=(V,E)是一个无向图,如果顶点V可分割为两个互不相交的子集(A,B),并且图中的每条边(i,j)所关联的两个顶点i和j分别属于这两个不同的顶点集,则称图G为二分图。

二分图判定:

定理:

一张无向图是二分图,当且仅当图中不存在奇环。

可用染色法来判断奇环是否存在。

bool flag=0;

void dfs(int x,int color)
{
    if(flag)
        return ;
    v[x]=color;
    int y;
    for(int i=head[x];i;i=Edge[i].nxt)//前向星存图
    {
        y=Edge[i].to;
        if(v[y]==0)
            dfs(y,3-color);
        else if(v[y]==color)
        {
            flag=1; //存在奇环 不是二分图
            return ;
        }
    }
}

for(int i=1;i<=n;i++)
    if(!v[i])
        dfs(i,1);

参考一:

https://blog.csdn.net/pi9nc/article/details/11848327

匈牙利算法:(求无边权二分图的最大匹配)

算法思想:匈牙利算法其实是在保证匹配不减少的情况下,试图改变以前的某些策略,让新加入的节点被匹配上。

dfs版本:(每个左侧点都要调用一次dfs(那么use数组是针对右侧点的) 若返回为true 说明当前点可以被匹配上)

bool dfs(int x)
{
    for (int i = 1; i <= sizey; i++)
    {
        if (use[i] == 0 && a[x][i])
        {
            use[i] = 1;
            if (pre[i] == 0 || dfs(pre[i]))
            {
                pre[i] = x;
                return true;
            }
        }
    }
    return false;
}

参考二:

https://blog.csdn.net/chenshibo17/article/details/79933191

KM算法:(带权二分图的最大权完美匹配)

bool findpath(int x)
{
    int tempDelta;
    visx[x]=true;
    for(int y=0;y<ny;++y)
    {
        if(visy[y])
            continue;
        tempDelta=lx[x]+ly[y]-G[x][y];
        if(tempDelta==0)
        {//(x,y)在相等子图中
            visy[y] = true;
            if(match[y] == -1 || findpath(match[y]))
            {
                match[y]=x;
                return true;
            }
        }
        else if(slack[y]>tempDelta)
            slack[y]=tempDelta;
        //(x,y)不在相等子图中且y不在交错树中
    }
    return false;
}
int KM()
{
    memset(match,-1,sizeof(match));
    for(int x=0;x<nx;++x)
    {
        memset(slack,INF,4*ny);
        while(true)
        {
            memset(visx,0,sizeof(visx));
            memset(visy,0,sizeof(visy));
            if(findpath(x))
                break;
            else
            {
                int delta=INF;
                for(int j=0;j<ny;++j)
                    if(!visy[j]&&delta>slack[j])
                        delta=slack[j];
                for(int i=0;i<nx;++i)
                    if(visx[i])
                        lx[i]-=delta;
                for(int j=0;j<ny;++j)
                {
                    if(visy[j])
                        ly[j]+=delta;
                    else
                        slack[j]-=delta;
                }
            }
        }
    }
    int ans=0;
    for(int i=0;i<ny;i++)
        ans+=G[match[i]][i];
    return ans;
}

补充:

最大匹配数:最大匹配的匹配边的数目

最小点覆盖数:选取最少的点,使任意一条边至少有一个端点被选择

最大独立数:选取最多的点,使任意所选两点均不相连

最小路径覆盖数:对于一个 DAG有向无环图),选取最少条简单路径,使得每个顶点属于且仅属于一条路径。路径长可以为 0(即单个点)。

最大匹配边数 = 最小点覆盖数

最大独立数 = 顶点数 - 最大匹配数

最小路径覆盖数 = 顶点数 - DAG图的拆点二分图的最大匹配数

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值