zoukankan      html  css  js  c++  java
  • poj 2195 Going Home (KM算法)

    呃,省赛已经过去了,生活应该继续~~ 好几天没写博了,一直在研究KM算法,今天算是有点小明白了,做了一道模板题练练手。

    先讲讲我理解的KM算法吧,如果你已经学会二分匹配中的匈牙利算法,那么要理解KM算法就很容易了,其实KM算法就是在匈牙利算法的基础上加上两点的权值。

    首先是相等子图的概念:设顶点Xi的顶标为A[ i ],顶点Yj的顶标为B[ j ],顶点XiYj之间的边权为w[i,j]若由二分图中所有满足A[ i ]+B[j]=w[i,j]的边(i,j)构成的子图称做相等子图。

    如果相等子图中有完备匹配,则这个完备匹配就是该二分图的最大权匹配。

    再解释下什么叫完备匹配:所谓的完备匹配就是在二部图中,X点集中的所有点都有对应的匹配或者是Y点集中所有的点都有对应的匹配,则称该匹配为完备匹配。

    这个定理是显然的。因为对于二分图的任意一个匹配,如果它包含于相等子图,那么它的边权和等于所有顶点的顶标和;如果它有的边不包含于相等子图,那么它的边权和小于所有顶点的顶标和。所以相等子图的完备匹配一定是二分图的最大权匹配。

      初始时为了使A[ i ]+B[j]>=w[i,j]恒成立,令A[ i ]为所有与顶点Xi关联的边的最大权,B[j]=0。如果当前的相等子图没有完备匹配,就按下面的方法修改顶标以使扩大相等子图,直到相等子图具有完备匹配为止。

      我们求当前相等子图的完备匹配失败了,是因为对于某个X顶点,我们找不到一条从它出发的交错路。这时我们获得了一棵交错树,它的叶子结点全部是X顶点。现在我们把交错树中X顶点的顶标全都减小某个值d,Y顶点的顶标全都增加同一个值d,那么我们会发现:

      1)两端都在交错树中的边(i,j),A[ i ]+B[j]的值没有变化。也就是说,它原来属于相等子图,现在仍属于相等子图。

      2)两端都不在交错树中的边(i,j),A[ i ]和B[j]都没有变化。也就是说,它原来属于(或不属于)相等子图,现在仍属于(或不属于)相等子图。

      3)X端不在交错树中,Y端在交错树中的边(i,j),它的A[ i ]+B[j]的值有所增大。它原来不属于相等子图,现在仍不属于相等子图。

      4)X端在交错树中,Y端不在交错树中的边(i,j),它的A[ i ]+B[j]的值有所减小。也就说,它原来不属于相等子图,现在可能进入了相等子图,因而使相等子图得到了扩大。

      现在的问题就是求d值了。为了使A[ i ]+B[j]>=w[i,j]始终成立,且至少有一条边进入相等子图,d应该等于:

      Min{A[ i ]+B[j]-w[i,j] | Xi在交错树中,Yi不在交错树中}。

     

    看看代码比较好理解:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <math.h>
    #include <iostream>
    #include <algorithm>
    #define maxx 201
    #define INF 0xffffff
    using namespace std;
    
    struct node
    {
        int x ;
        int y ;
    }h[maxx] , p[maxx] ;
    int mm[maxx][maxx] ;
    int vistx[maxx] ,visty[maxx] ;
    int lx[maxx] , ly[maxx] ;//顶标
    int match[maxx] ;
    int tn , tm ;
    
    int find ( int x )
    {
        int i ;
        vistx[x] = 1;
        for ( i = 0 ; i < tm ; i++ )
        {
            if ( !visty[i] && lx[x] + ly[i] == mm[x][i] )
            {
                visty[i] = 1;
                if ( match[i] == -1 || find ( match[i] ))
                {
                    match[i] = x;
                    return 1;
                }
            }
        }
        return 0;
    }
    
    int main()
    {
        int n , m , i , j , k ;
        char str[maxx];
    
        while ( scanf ( "%d%d" , &n , &m ) , n + m )
        {
            tn = tm = 0;
            for ( i = 0 ; i < n ; i++ )//存储H和m的位置
            {
                cin>>str;
                for ( j = 0 ; j < m ; j++ )
                {
                    if ( str[j] == 'H' )
                    {
                        h[tn].x = i;
                        h[tn].y = j;
                        tn++;
                    }
                    else if ( str[j] == 'm' )
                    {
                        p[tm].x = i ;
                        p[tm].y = j ;
                        tm++;
                    }
                }
            }
            memset( mm , 0 , sizeof ( mm ));//建立连接
            for ( i = 0 ; i < tn ; i++ )
            for ( j = 0 ; j < tm ; j++ )
            mm[i][j] = abs( h[i].x - p[j].x ) + abs( h[i].y - p[j].y );
            memset( lx , 1 , sizeof( lx ));//初始化lx;
            memset( ly , 0 , sizeof( ly ));//
            for ( i = 0 ; i < tn ; i++ )
            for ( j = 0 ; j < tm ; j++ )
            if ( mm[i][j] < lx[i] )
            lx[i] = mm[i][j] ;//如果是最大权值匹配 则初始值顶标取最大值
                            //若是最小匹配则取最小值
            memset( match , -1 , sizeof ( match ));
            for ( i = 0 ; i < tn ; i++ )
            {
                for ( ; ; )
                {
                    memset( vistx , 0 , sizeof ( vistx ));
                    memset( visty , 0 , sizeof ( visty ));
                    if ( find ( i ))//寻找完备匹配 
                    break;
                    int minn = INF ;
                    for ( j = 0 ; j < tn ; j++ )
                    {
                        if ( vistx[j] )//xj点在搜索数上
                        {
                            for ( k = 0 ; k < tm ; k++ )
                            if ( !visty[k] && mm[j][k] - lx[j] -ly[k] < minn )//yk点不在搜索树上,
                            minn = mm[j][k] - lx[j] - ly[k]; //找出顶标最大能改进的d值
                        }
                    }
                    for ( j = 0 ; j < tn ; j++ )//用d来改进搜索树上各点的顶标
                    if ( vistx[j])
                    lx[j] += minn ;
                    for ( j = 0 ; j < tm ; j++ )
                    if ( visty[j] )
                    ly[j] -= minn;
                }
            }
            int sum = 0;
            for ( i = 0 ; i < tm ; i++ )
            sum += mm[match[i]][i];
            cout<<sum<<endl;
        }
    }

     

     

  • 相关阅读:
    leetcode--Populating Next Right Pointers in Each Node II
    leetcode—Populating Next Right Pointers in Each Node
    Pascal's Triangle II
    leetcode—pascal triangle
    leetcode—triangle
    October 23rd, 2017 Week 43rd Monday
    October 22nd, 2017 Week 43rd Sunday
    October 21st 2017 Week 42nd Saturday
    October 20th 2017 Week 42nd Friday
    October 19th 2017 Week 42nd Thursday
  • 原文地址:https://www.cnblogs.com/misty1/p/2506785.html
Copyright © 2011-2022 走看看