//二分图最小权匹配 km算法 //没编过km,上百科找的最大权匹配,改成最小权 //最大权原理:将km1[i]置为最大权关联边 km2[i]为0,利用条件 km1[i] + km2[j] >= w[i][j] //每个点都找满足km1[i] + km2[j] == w[i][j] 的交叉路 就完了 //找不到 : _used[x] += d _used[y] -= d d = min(km1[i]+km2[j] - w[i][j]) //最小权看代码就可以。。 #include <iostream> #include <cstring> #include <cstdio> #define _abs( x ) ((x) > 0 ? (x) : -(x)) #define INF (1<<29) using namespace std; int n,m,_m,_h,d,result; char path; int w[101][101]; int km1[101],km2[101]; bool _used1[101],_used2[101]; int linky[101]; struct Pair { void P(int a,int b) { x = a;y = b; } int x,y; }p1[101],p2[101]; bool find( int x ) { _used1[x] = true; for( int i = 1;i <= _m;i++ ) { if( _used2[i] ) continue; int t = w[x][i] - km1[x] - km2[i]; if( t == 0 ) { _used2[i] = true; //忘记加这句悲剧了会 if( !linky[i] || find( linky[i] ) ) { linky[i] = x; return true; } } else if( d > t ) d = t; } return false; } int main() { //freopen( "1.txt","r",stdin ); while( scanf("%d%d",&n,&m) && 0 != n ) { memset( linky,0,sizeof( linky ) ); memset( km1,0x7f,sizeof(km1) ); //最大权的话置 0 memset( km2,0,sizeof(km2) ); _m = _h = result = 0; for( int i = 1;i <= n;i++ ) { for( int j = 1;j <= m;j++ ) { cin>>path; if( path == 'm' ) { p1[++_m].P(i,j); } else if( path == 'H' ) { p2[++_h].P(i,j); } } } for( int i = 1;i <= _m;i++ ) for( int j = 1;j <= _h;j++ ) { w[i][j] = _abs( (p1[i].x - p2[j].x) ) + _abs( (p1[i].y - p2[j].y) ); if( km1[i] > w[i][j] ) km1[i] = w[i][j]; //保证条件 km1[i] + km2[j] <= w[i][j] 最大权反之 } for( int i = 1;i <= _m;i++ ) { while( true ) { memset( _used1,false,sizeof(_used1) ); memset( _used2,false,sizeof(_used2) ); d = INF; if( find( i ) ) break; for( int j = 1;j <= _m;j++ ) { if( _used1[j] ) km1[j] += d; //最大权处理时反之 if( _used2[j] ) km2[j] -= d; } } } for( int i = 1;i <= _m;i++ ) result += ( km1[i] + km2[i] ); cout<< result <<endl; } return 0; }