• POJ 2195(KM模板题)


    题意简述:

    比较好理解,给定一个N*M的地图,地图上有若干个man和house,且man与house的数量一致。man每移动一格需花费$1(即单位费用=单位距离),一间house只能入住一个man。现在要求所有的man都入住house,求最小费用。(题意转自:http://blog.csdn.net/lyy289065406/article/details/6732762

    思路:

    嗯,这个见图可以说没什么难度吧,明摆着怎么建图么。。需要注意的是,KM求的是最大权匹配,而题木要求是最小,这个好处理,把边的权值全部取相反数,然后做KM,最后把ans取相反数即可

    View Code
     1 #include <cstdio>
     2 #include <cstdlib>
     3 #include <cstring>
     4 #include <cmath>
     5 #define INF 10000000
     6 using namespace std;
     7 char ls[120][120];
     8 int map[120][120],ax[120],bx[120],ay[120],by[120],n,m,cnta,cntb,slack[120],lx[120],ly[120],linky[120];
     9 bool visx[120],visy[120];
    10 void read()
    11 {
    12     memset(map,0,sizeof map);
    13     cnta=cntb=0;
    14     for(int i=1;i<=n;i++) scanf("%s",ls[i]+1);
    15     for(int i=1;i<=n;i++)
    16         for(int j=1;j<=m;j++)
    17         {
    18             if(ls[i][j]=='m')
    19             {
    20                 cnta++;
    21                 ax[cnta]=i; ay[cnta]=j;
    22             }
    23             else if(ls[i][j]=='H')
    24             {
    25                 cntb++;
    26                 bx[cntb]=i; by[cntb]=j;
    27             }
    28         }
    29     for(int i=1;i<=cnta;i++)
    30         for(int j=1;j<=cntb;j++)
    31             map[i][j]=-(abs(ax[i]-bx[j])+abs(ay[i]-by[j]));
    32 }
    33 bool find(int u)
    34 {
    35     visx[u]=true;
    36     for(int i=1;i<=cntb;i++)
    37     {
    38         if(visy[i]) continue;
    39         int t=lx[u]+ly[i]-map[u][i];
    40         if(t==0)
    41         {
    42             visy[i]=true;
    43             if(linky[i]==-1||find(linky[i]))
    44             {
    45                 linky[i]=u;
    46                 return true;
    47             }
    48         }
    49         else if(slack[i]>t) slack[i]=t;
    50     }
    51     return false;
    52 }    
    53 void KM()
    54 {
    55     memset(linky,-1,sizeof linky);
    56     memset(lx,0,sizeof lx);
    57     memset(ly,0,sizeof ly);
    58     for(int i=1;i<=cnta;i++)
    59         for(int j=1;j<=cntb;j++)
    60             if(map[i][j]>lx[i]) lx[i]=map[i][j];
    61     for(int k=1;k<=cnta;k++)
    62     {
    63         for(int i=1;i<=cnta;i++) slack[i]=INF;
    64         while(1)
    65         {
    66             memset(visx,0,sizeof visx);
    67             memset(visy,0,sizeof visy);
    68             if(find(k)) break;
    69             int d=INF;
    70             for(int i=1;i<=cntb;i++)
    71                 if(!visy[i]&&d>slack[i]) d=slack[i];
    72             for(int i=1;i<=cnta;i++)
    73                 if(visx[i]) lx[i]-=d;
    74             for(int i=1;i<=cntb;i++)
    75             {
    76                 if(visy[i]) ly[i]+=d;
    77                 else slack[i]-=d;
    78             }   
    79         }
    80     }
    81 }
    82 void getans()
    83 {
    84     int ans=0;
    85     for(int i=1;i<=cnta;i++) ans+=map[linky[i]][i];
    86     printf("%d\n",-ans);
    87 }
    88 int main()
    89 {
    90     while(scanf("%d%d",&n,&m))
    91     {
    92         if(n==0&&m==0) break;
    93         read();
    94         KM();
    95         getans();
    96     }
    97     return 0;
    98 }

    算法拓展:

    [二分图带权匹配与最佳匹配]

    什么是二分图的带权匹配?二分图的带权匹配就是求出一个匹配集合,使得集合中边的权值之和最大或最小。而二分图的最佳匹配则一定为完备匹配,在此基础上,才要求匹配的边权值之和最大或最小。二分图的带权匹配与最佳匹配不等价,也不互相包含

    我们可以使用KM算法实现求二分图的最佳匹配。方法我不再赘述,可以参考tianyi的讲解。KM算法可以实现为O(N^3)。

    [KM算法的几种转化]

    KM算法是求最大权完备匹配,如果要求最小权完备匹配怎么办?方法很简单,只需将所有的边权值取其相反数,求最大权完备匹配,匹配的值再取相反数即可。

    KM算法的运行要求是必须存在一个完备匹配,如果求一个最大权匹配(不一定完备)该如何办?依然很简单,把不存在的边权值赋为0。

    KM算法求得的最大权匹配是边权值和最大,如果我想要边权之积最大,又怎样转化?还是不难办到,每条边权取自然对数,然后求最大和权匹配,求得的结果a再算出e^a就是最大积匹配。至于精度问题则没有更好的办法了。

    [求最小(大)权匹配的费用流建模方法]

    求最小(大)权匹配,可以用最小(大)费用最大流的方法。和二分图最大匹配的构图方法类似,添加附加源S和附加汇T,从S向二分图X集合中每个顶点连接一条权值为0,容量为1的有向边,从Y集合中每个顶点向T也连接一条权值为0,容量为1的有向边。然后把原有的边变成容量为1,权值不变的有向边。求从S到T的最小(大)费用最大流,就能求得最小(大)权匹配。

    上述建模求最大权匹配的方法求得的一定是最佳匹配(如果存在完备匹配),因为S到X集合每条边全部满流。如下图所示,最小费用最大流为2。

    km1

    要求最大权匹配(不一定完备匹配)。如下图,只需再引入一个顶点A,从X集合的每个顶点向A连接一条容量为1,权值为0的边,然后再由A向T连接一条权值为0,容量不小于|X|的边,求最大费用最大流,这时是100。

    km2

    最小权匹配也类似,不过新加的边权要为一个极大值,大于所有已有边权值。

    [KM算法与费用流的比较]

    从理论上分析,KM算法的时间复杂度比费用流要好,但是实际上和较好的费用流算法比起来运行效率是差不多的,KM算法优势仅仅在于编程容易。KM算法也有其不可避免的局限性,就是必须用邻接矩阵来表示。这样会浪费很多的空间,尤其是图相当稀疏的时候。而对于十分稀疏的图,许多优秀的费用流算法效率是很高的。这并不说明KM算法不如费用流,毕竟在信息学竞赛中,编程的复杂度也是一个相当重要的需要考虑的因素。

    (转自:http://www.byvoid.com/blog/match-km/

     

     

    具体模板和讲解请看:

    http://cuitianyi.com/blog/%E6%B1%82%E6%9C%80%E5%A4%A7%E6%9D%83%E4%BA%8C%E5%88%86%E5%8C%B9%E9%85%8D%E7%9A%84km%E7%AE%97%E6%B3%95/

     

    在此感谢以上两位大神的文章!

    没有人能阻止我前进的步伐,除了我自己!
  • 相关阅读:
    NPOI 操作 excel 帮助类
    文件帮助类
    浮点数精度问题
    多段文本显示省略号
    数字排序
    删除字符串首位空格
    生成一定范围的随机数
    锚链接动画
    原生js转json
    弹出遮罩和对话框
  • 原文地址:https://www.cnblogs.com/proverbs/p/2657854.html
Copyright © 2020-2023  润新知