poj2195最小权值二分匹配KM算法

poj2195KM算法

== 我只是自己看看的 ==

具体要看km是怎么弄的请看此链接:
这是我觉得网络上将的最让我懂的
http://philoscience.iteye.com/blog/1754498
然后普通的KM是n4,这题要用n3的才能过,n4我超时

这题只要构图正确,弄出二维矩阵,直接扔到模板上就出结果了

代码部分

我给下输入的构图代码

char s[MAXN];
    int i, j, res;
    while (scanf("%d%d", &n, &m) && (n + m))
    {
        H = M = res = 0;
        for (i = 0; i < n; i++)
        {
            scanf("%s", s);
            for (j = 0; j < m; j++)
            {
                if (s[j] == 'H')
                    house[H].r = i, house[H++].c = j;
                else if (s[j] == 'm')
                    man[M].r = i, man[M++].c = j;
            }
        }
        for (i = 0; i < M; i++) //求最小带权匹配可以将权值改为负数  
        {
            for (j = 0; j < H; j++)
            {
                map[i][j] = abs(man[i].r - house[j].r) + abs(man[i].c - house[j].c);
            }
        }

KM的代码

/*
 *  KM算法,求最大或最小权二分匹配,用二维矩阵完成
 */

int map[MAXN][MAXN];//二维矩阵权值
int A[MAXN];//A的标记值
int B[MAXN];//B的标记值
bool visA[MAXN];
bool visB[MAXN];
int match[MAXN];//匹配的点
int slack[MAXN];

bool find_path(int i)//找路径
{
    visA[i] = true;
    for (int j = 0; j < H; j++)
    {
        if (!visB[j] && A[i] + B[j] == map[i][j])
        {
            visB[j] = true;
            if (match[j] == -1 || find_path(match[j]))
            {
                match[j] = i;
                return true;
            }
        }
        else if (A[i] + B[j] > map[i][j]) //j属于B,且不在交错路径中  
            slack[j] = min(slack[j], A[i] + B[j] - map[i][j]);
    }
    return false;
}
void KM(int num)//true求最大权,false求最小权,num要匹配的对数
{
    int i, j, d;
    memset(A, 0, sizeof(A));
    memset(B, 0, sizeof(B));
    memset(match, -1, sizeof(match));
    for (i = 0; i < num; i++)
    {
        for (j = 0; j < num; j++)
        {
            A[i] = max(map[i][j], A[i]);
        }
    }
    for (i = 0; i < num; i++)
    {
        for (j = 0; j < num; j++)
            slack[j] = inf;
        while (1)
        {
            memset(visA, 0, sizeof(visA));
            memset(visB, 0, sizeof(visB));
            if (find_path(i))
                break; //返回true退出,从i点出发找到交错路径则跳出循环  
            for (d = inf, j = 0; j < num; j++) //取最小的slack[j]  
            {
                if (!visB[j] && d > slack[j])
                    d = slack[j];
            }
            for (j = 0; j < num; j++) //集合A中位于交错路径上的-d  
            {
                if (visA[j]) A[j] -= d;
            }
            for (j = 0; j < num; j++) //集合B中位于交错路径上的+d  
            {
                if (visB[j])
                    B[j] += d;
                else
                    slack[j] -= d; //注意修改不在交错路径上的slack[j] 
            }
        }
    }
}
int Kuhn_Munkras(bool maxweight, int num)
{
    if (!maxweight)
    { //如果求最小匹配,则要将边权取反  
        for (int i = 0; i < num; i++)
        {
            for (int j = 0; j < num; j++)
            {
                map[i][j] = -map[i][j];
            }
        }
    }
    KM(num);
    int sum = 0;
    for (int i = 0; i < num; i++)
    {
        if (match[i] >= 0)
        {
            sum += map[match[i]][i];
        }
    }
    if (!maxweight)
        sum = -sum;
    return sum;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值