poj2195

                                                                                                   Going Home
Time Limit: 1000MS   Memory Limit: 65536K
Total Submissions: 15624   Accepted: 7996

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


/*KM带权匹配 */
#include<iostream>
using namespace std;
const int N=110;
const int INF=0x7FFFFFFF;
int g[N][N];                   //关系网
bool vx[N],vy[N];              //x集合和Y集合是否访问过
int lx[N],ly[N],linky[N];      //顶标和y的匹配
int n,m,h,p;                   //原图的大小和人、屋的数目

struct Man{                    //人的坐标
    int x,y;
}man[N];

struct House{                  //屋的坐标
    int x,y;
}house[N];

bool DFS(int x){               //寻找匹配
    vx[x]=true;                //标记为访问过,避免重复访问,同时为以后找增广路做标记
    for(int y=0;y<p;y++){      //遍历可以与x点匹配的y
        if(vy[y]) continue;    //如果y被访问过表示在本轮深搜中发现该点已被匹配
        if(lx[x]+ly[y]==g[x][y]){   //如果该匹配符合最大值
            vy[y]=true;             //则标记ture,表示尝试过匹配此点
            if(linky[y]==-1||DFS(linky[y])){ //若该点未被匹配 或 其他点可以让出该点
                linky[y]=x;         //把y匹配给x
                return true;        //返回本轮匹配成功
            }
        }
    }
    return false;             //失败
}

int KM(){
    int i,lack;              
    memset(linky,255,sizeof(linky));     //表示y与哪个X匹配,初始化-1表示未匹配
    memset(ly,0,sizeof(ly));             //所有ly顶标为0
    for(i=0;i<h;i++){
        lx[i]=-INF;                      
        for(int j=0;j<p;j++)
            if(g[i][j]>lx[i]) lx[i]=g[i][j];//所有lx顶标为自己能匹配的最大值,以后往次大值匹配
    }
    
    for(int x=0;x<h;x++){                 //历遍所有X集合的点
        while(1){
            memset(vx,0,sizeof(vx));      //搜索点标记为未被访问过
            memset(vy,0,sizeof(vy));     
            lack=INF;                     //用于查找最小的调整量
            if(DFS(x)) break;             //如果匹配成功,进入下一点
            for(i=0;i<h;i++)
            {             //若匹配不成功,把有交错的点进行调整
                if(vx[i])
                {                //搜索中访问过的点都为交错点
                     for(int j=0;j<p;j++)
                    { //在交错点中查找没匹配的最大边
                        if(!vy[j]&&lack>lx[i]+ly[j]-g[i][j])
                            lack=lx[i]+ly[j]-g[i][j];
                            
                    }
                }
            }
            /*
               注意,在查找中,访问过的X和Y都复合lx[i]+ly[j]==g[i][j]
               所以在减少lx[i]的同时增大ly[j],总和不变,不影响当前最大匹配
               因为调整的大小是由lack=lx[i]+ly[j]-g[i][j]得来,
               所以必定能找到一条边使lx[i](交错点)+ly[j](未匹配的点)=g[i][j](匹配)
            */
            for(i=0;i<h;i++){             //对x集合顶标进行缩小
                if(vx[i]) lx[i]-=lack;
            }
            for(i=0;i<p;i++){             //对Y集合顶标增大。
                if(vy[i]) ly[i]+=lack;
            }
        }
    }
    int ans=0;
    for(i=0;i<p;i++)
    {                   //历遍所有Y集合
        if(linky[i]!=-1)
        {               //若该Y与X匹配
            ans+=g[linky[i]][i];        //总和相加
        }
    }
    return ans;
}

int dis(House p1,Man p2){               //两点距离
    int len = (p1.x-p2.x) > 0 ? (p1.x-p2.x) : (p2.x-p1.x);
    len += (p1.y-p2.y) > 0 ? (p1.y-p2.y) : (p2.y-p1.y);
    return len;
}

int main(){
    char str[N];
    int i,j;
    while(scanf("%d%d",&n,&m) && n+m){
        h=p=0;
        for(i=0;i<n;i++){
            scanf("%s",str);
            for(j=0;j<m;j++){
                if(str[j] == 'H'){
                    house[h].x = i;
                    house[h++].y = j;
                }
                else if(str[j] == 'm'){
                    man[p].x = i;
                    man[p++].y = j;
                }
            }
        }
        for(i=0;i<h;i++){               //构图
            for(j=0;j<p;j++){
                g[i][j] = -dis(house[i],man[j]);//求最小匹配所以权值取负
            }
        }
        printf("%d\n",-KM());//取反得出最小匹配
    }
    return 0;
}

 

posted @ 2013-08-06 09:58  哥的笑百度不到  阅读(159)  评论(0编辑  收藏  举报