1. 程式人生 > >Going Home(POJ-2195)

Going Home(POJ-2195)

Problem Description

On a grid map there are n little men and n houses. In each unit time, every little man can move one unit step, either horizontally, or vertically, to an adjacent point. For each little man, you need to pay a $1 travel fee for every step he moves, until he enters a house. The task is complicated with the restriction that each house can accommodate only one little man. 

Your task is to compute the minimum amount of money you need to pay in order to send these n little men into those n different houses. The input is a map of the scenario, a '.' means an empty space, an 'H' represents a house on that point, and am 'm' indicates there is a little man on that point. 

You can think of each point on the grid map as a quite large square, so it can hold n little men at the same time; also, it is okay if a little man steps on a grid with a house without entering that house.

Input

There are one or more test cases in the input. Each case starts with a line giving two integers N and M, where N is the number of rows of the map, and M is the number of columns. The rest of the input will be N lines describing the map. You may assume both N and M are between 2 and 100, inclusive. There will be the same number of 'H's and 'm's on the map; and there will be at most 100 houses. Input will terminate with 0 0 for N and M.

Output

For each test case, output one line with the single integer, which is the minimum amount, in dollars, you need to pay.

Sample Input

2 2
.m
H.
5 5
HH..m
.....
.....
.....
mm..H
7 8
...H....
...H....
...H....
mmmHmmmm
...H....
...H....
...H....
0 0

Sample Output

2
10
28

————————————————————————————————————————————————————

題意:給一 n*m 的地圖,地圖上有若干個人和房子,且人和房子數量相同,人每移動一格需要花費 1 費用,一個房子只能住一個人,現在要讓所有的人都入住房子,求花費的最小費用

思路:人為左點集,房子為右點集,每個人與每個房子間都有一條邊,邊的權值是人到房子的距離,現要讓每個人都進入一房間,且移動的費用最小,實質就是求二分圖的一個完全匹配,且匹配點的邊權和最小

使用 KM 演算法只能求二分圖的最優匹配,即邊權和最大的完全匹配, 這裡有一個技巧,就是將所有的邊權取負,再進行 KM 演算法,得到的解取負就是邊權和最小的完全匹配

假設存在一個最優解 res,是所有解中花費最小的,那麼 -res 自然是所有花費中最大的解,當將所有邊權取負後,用 KM 演算法得到的最優匹配必然是那個花費最大的解,取負後就是所需的最小邊權值的解

Source Program

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
#define PI acos(-1.0)
#define E 1e-6
#define MOD 16007
#define INF 0x3f3f3f3f
#define N 1001
#define LL long long
using namespace std;
int n;
int G[N][N];
int Lx[N],Ly[N];
bool visX[N],visY[N];
int linkX[N],linkY[N];
bool dfs(int x){
    visX[x]=true;
    for(int y=1;y<=n;y++){
        if(!visY[y]){
            int temp=Lx[x]+Ly[y]-G[x][y];
            if(temp==0){
                visY[y]=true;
                if(linkY[y]==-1 || dfs(linkY[y])){
                    linkY[y]=x;
                    linkX[x]=y;
                    return true;
                }
            }
        }
    }
    return false;
}
void update(){
    int minn=INF;
    for(int i=1;i<=n;i++){
        if(visX[i]){
            for(int j=1;j<=n;j++){
                if(!visY[j]){
                    minn=min(minn,Lx[i]+Ly[j]-G[i][j]);
                }
            }
        }
    }
    for(int i=1;i<=n;i++){
        if(visX[i])
            Lx[i]-=minn;
        if(visY[i])
            Ly[i]+=minn;
    }
}
int KM(){
    memset(linkX,-1,sizeof(linkX));
    memset(linkY,-1,sizeof(linkY));


    for(int i=1;i<=n;i++){
        Lx[i]=Ly[i]=0;
        for(int j=1;j<=n;j++)
            Lx[i]=max(Lx[i],G[i][j]);
    }

    for(int i=1;i<=n;i++){
        while(true){
            memset(visX,false,sizeof(visX));
            memset(visY,false,sizeof(visY));

            if(dfs(i))
                break;
            else
                update();
        }
    }

    int ans=0;
    for(int i=1;i<=n;i++)
        ans+=G[linkY[i]][i];

    return ans;
}
struct Node{
    int x;
    int y;
}nodeMan[N],nodeHouse[N];
int main(){
    int r,c;
    while(scanf("%d%d",&r,&c)!=EOF&&(r+c)){

        int numMan=0,numHouse=0;
        for(int i=1;i<=r;i++){
            for(int j=1;j<=c;j++){
                char c;
                cin>>c;
                if(c=='H'){
                    numHouse++;
                    nodeHouse[numHouse].x=i;
                    nodeHouse[numHouse].y=j;
                }
                if(c=='m'){
                    numMan++;
                    nodeMan[numMan].x=i;
                    nodeMan[numMan].y=j;
                }
            }
        }

        for(int i=1;i<=numMan;i++){
            for(int j=1;j<=numHouse;j++){
                int x=abs(nodeMan[i].x-nodeHouse[j].x);
                int y=abs(nodeMan[i].y-nodeHouse[j].y);
                G[i][j]=-(x+y);
            }
        }

        n=numMan;
        int res=KM();
        printf("%d\n",-res);
    }
    return 0;
}