这道题的题意是:在一个矩阵空间中,分布着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的错误不能忍啊!!!
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