poj2195_KM最小权值和

这道题的题意是:在一个矩阵空间中,分布着m个人和m个房子,每个房子中要安排一个人,每个人移动一步需要1美元,求为每个人安排房子后所需要的金钱的最小值。

分析:这道题乍一看是二分图匹配(还可以用网络流做,现在还不会啊),但是我们学过的KM算法求的是权值最大的匹配,这里求的是权值最小的匹配。怎么办呢?昨天学习了KM算法,如果对原理熟悉的话,其实最大权值和最小权值的道理一样。

KM最小权值和算法的原理:

1.顶点集分为A,B两个集合,初始化顶标Ai为与Ai相连的边的最小权值,Bi为0.在算法的整个过程中,保证Wij>=Ai+Bi。

2.相等子图:如果二分图的某个子图中每条匹配满足Wij=Ai+Bj,那个这个子图就是相等子图。如果相等子图中存在完美匹配的话,这个相等子图中的权值和即为最小权值和。

这个定理的证明:在整个算法的过程中,就是通过修改顶标找寻Wij=Ai+Bj的匹配,直到图中所有的匹配都满足这一条件,而一开始Wij>=Ai+Bj,所以算法结束后Wij之和具有最小的权值。

3.当某次找寻增广路失败时,此时找到的是一颗交错树,交错树的叶子都在A集合中,我们需要更新顶标值,让交错树A集合中的点加上d(这里与最大权值相反),对应B集合中的点减去d,这样可以让交错树A集合中的某些点形成Wij=Ai+Bj的情况,从而得到匹配。

4.每次的d是此次遍历过程中的Min{Wij-Ai-Bj}.

在这道题中,也没有必要存储结构,只需要将人和房子的坐标记录下来即可,然后建图。

以下是代码,昨晚写的,那个把j写成i的错误不能忍啊!!!

View Code
  1 #include <iostream>
  2 #include <memory.h>
  3 #include <stdio.h>
  4 #include <math.h>
  5 #include <stdlib.h>
  6 using namespace std;
  7 const int maxnum=999999;
  8 typedef struct
  9 {
 10     int x;
 11     int y;
 12 }rr;
 13 rr man[101],house[101];
 14 
 15 int array[101][101];
 16 int res[101];
 17 int muse[101];
 18 int huse[101];
 19 int mvalue[101];
 20 int hvalue[101];
 21 int v,d;
 22 
 23 bool find(int i)
 24 {
 25     muse[i]=true;
 26     int j;
 27     for(j=1;j<=v;j++)
 28     {
 29         if(huse[j]) continue;
 30         if(array[i][j]==0) continue;
 31         int t=array[i][j]-mvalue[i]-hvalue[j];
 32         if(t==0)
 33         {
 34             huse[j]=true;
 35             if(res[j]==0 || find(res[j]))
 36             {
 37                 res[j]=i;
 38                 return true;
 39             }
 40         }
 41         else if(d>t)
 42             d=t;
 43     }
 44     return false;
 45 }
 46 
 47 int main()
 48 {
 49     int m,n,i,j;
 50     char ch;
 51     int result;
 52     int cntman,cnthouse;
 53 
 54     while(scanf("%d%d",&m,&n)!=EOF)
 55     {
 56         if(m==0 && n==0) break;
 57         memset(array,0,sizeof(array));
 58         memset(res,0,sizeof(res));
 59         cntman=1;
 60         cnthouse=1;
 61         result=0;
 62         getchar();
 63         for(i=0;i<m;i++)
 64         {
 65             for(j=0;j<n;j++)
 66             {
 67                 scanf("%c",&ch);
 68                 if(ch=='m')
 69                 {
 70                     man[cntman].x=i;
 71                     man[cntman].y=j;
 72                     cntman++;
 73                 }
 74                 else if(ch=='H')
 75                 {
 76                     house[cnthouse].x=i;
 77                     house[cnthouse].y=j;
 78                     cnthouse++;
 79                 }
 80             }
 81             if(j==n) getchar();
 82         }
 83 
 84         v=cntman-1;
 85         for(i=1;i<=v;i++)  //建图
 86         {
 87             for(j=1;j<=v;j++)
 88             {
 89                 int len=abs(man[i].x-house[j].x)+abs(man[i].y-house[j].y);
 90                 array[i][j]=len;
 91             }
 92         }
 93 
 94         for(i=1;i<=v;i++)
 95         {
 96             mvalue[i]=maxnum;
 97             hvalue[i]=0;
 98         }
 99 
100         for(i=1;i<=v;i++)
101             for(j=1;j<=v;j++)
102             {
103                 if(mvalue[i]>array[i][j])
104                     mvalue[i]=array[i][j];
105             }
106 
107 
108         for(i=1;i<=v;i++)
109         {
110             while(1)
111             {
112                 memset(muse,false,sizeof(muse));
113                 memset(huse,false,sizeof(huse));
114                 d=maxnum;
115                 if(find(i))
116                     break;
117                 for(j=1;j<=v;j++)  //!!!
118                 {
119                     if(muse[j])   //交错树中的叶子
120                         mvalue[j]+=d;
121 
122                     if(huse[j])    //交错树中B中的点
123                         hvalue[j]-=d;
124                 }
125             }
126         }
127 
128         for(i=1;i<=v;i++)
129             result+=array[res[i]][i];
130         printf("%d\n",result);
131     }
132 
133 
134     return 0;
135 }

这道题也是tju oj1636

 

 

转载于:https://www.cnblogs.com/pushing-my-way/archive/2012/07/20/2600512.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值