KM 算法

有一份非常好的教程,但是我在百度文库看的,链接如下 力荐!!!!!!!!!

http://wenku.baidu.com/link?url=yKLV9Z1UyA3SCZqcZkDM0miWl5LWLgEJvOh_cY-iPQRIOP23sWg2sNgP_2-is2h_YAJ3yBboNyjyFNG_2N3-uUGbDf13kSgFFQjyOJruK1O

贴个模板,上面我有比较详细的注释

//KM最大最小匹配
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
#define INF 999999
#define MAX 5110
int n,match[MAX];
//match[v]=u, Y中点v和X中点u匹配,
//同时match[v]==-1说明v还没有访问
bool sx[MAX],sy[MAX];
//sx[i]==true x中的i被匹配过,sy同--用于标记是不是在交错树中
int lx[MAX],ly[MAX],mat[MAX][MAX];

bool path(int u)
{
    sx[u]=true;
    for(int v=0;v<n;v++)//比匈牙利多了一个构成相等子图的判断
        if(!sy[v]&&lx[u]+ly[v]==mat[u][v])
        {
            sy[v]=true;
            if(match[v]==-1||path(match[v]))
            {//v还未被匹配或者v匹配了match[v],
             //但是可以把match[v]匹配给其他的,那么就把v匹配给u
                match[v]=u;
                return true;
            }
        }
    return false;
}
//true 是最大匹配 , false是最小匹配, 不错的思想
//没有完美匹配的解,就返回 INF
int KM(bool truth)//可以不用更改地处理最小或最大权匹配
{
    int i,j;
    if(!truth)//如果是求得最小匹配
    {
        for(i=0;i<n;i++)
            for(j=0;j<n;j++)
                mat[i][j]=-mat[i][j];
    }
    //第一步:初始化顶标,lx[i]=max(w[i][j]),ly[i]为零
    for(i=0;i<n;i++)
    {
        lx[i]=-INF;
        ly[i]=0;
        for(j=0;j<n;j++)
            lx[i]=max(lx[i], mat[i][j]);
            //if(lx[i]<mat[i][j])
               // lx[i]=mat[i][j];
    }

    memset(match,-1,sizeof(match));
    for(int u=0;u<n;u++)
        while(1)
        {
            //匈牙利找完备匹配
            memset(sx,0,sizeof(sx));
            memset(sy,0,sizeof(sy));
            if(path(u)) break;//某个点找到匹配 进行下一个点
            //没有找到匹配,就lx[i]-d,ly[i]+d
            //求d,方法:
            int dmin=INF;
            for(i=0;i<n;i++)
                if(sx[i])
                    for(j=0;j<n;j++)
                        if(!sy[j])
                            dmin=min(lx[i]+ly[j]-mat[i][j],dmin);
            //修改lx[i]和ly[i],d[i]=min(A[i]+B[j]-w[i][j],i在交错树,y不在交错树)
            //这种求法保证了A[i]+B[j]>=w[i][j],并且至少有一条边进入相等子图
            //--就是所选的最小的d就等于A[ik]+B[jk]-w[ik][jk],那么A[ik]-=d之后,
            //A[ik]+B[jk]-w[ik][jk]==0;就是说至少边(i,j)进入子图
            for(i=0;i<n;i++)
            {
                if(sx[i])
                    lx[i]-=dmin;
                if(sy[i])
                    ly[i]+=dmin;
            }
        }
        int sum=0;//必要的时候看看这里会不会爆掉
        for(j=0;j<n;j++){
			if(mat[match[j]][j]==-INF)
				return INF;
            sum+=mat[match[j]][j];
        }
		if(!truth)//恢复
        {
            sum=-sum;
            for(i=0;i<n;i++)
                for(j=0;j<n;j++)
                    mat[i][j]=-mat[i][j];
        }
        return sum;
}
void Map_Init(int m,int n)
{
    int x,y,w;
    int i,j;
    for(i=0;i<n;i++)
        for(j=0;j<n;j++)
            mat[i][j]=INF;
        for(i=0;i<m;i++)
        {
            scanf("%d%d%d",&x,&y,&w);
            if(mat[x-1][y-1]>w)//处理重边
                mat[x-1][y-1]=w;
        }
}
int main()
{
    int m,i,j,a,b,c;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        Map_Init(m,n);
        int ans=KM(false);
        if(ans<INF) printf("%d\n",ans);
        else printf("-1\n");
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值