POJ 2195
//题目类型:二分匹配-最小权完美匹配
//算法实现:可以采用KM算法或者最小费用最大流实现
//KM算法实现 
#include <iostream>
#include <string>
#include <math.h>
//#include <conio.h>
using namespace std;
#define arraysize 105
int maxData = 1000000000;     
int w[arraysize][arraysize];
int match[arraysize];
int lx[arraysize],ly[arraysize],slack[arraysize];
bool finalx[arraysize],finaly[arraysize];
int hcount,mcount;      //由题目可知,hcount=mcount,所以直接使用KM算法进行最大权完美匹配 
typedef struct house
{
    int row;
    int col;
}house;
typedef struct littleman
{
    int row;
    int col;
}littleman;
littleman mans[arraysize];
house houses[arraysize];
bool DFS(int p)
{
     int i,j,t;
     finalx[p] = true;
     for(i=1;i<=hcount;++i)
     {
         if(finaly[i]) continue;
         int temp = lx[p]+ly[i]-w[p][i];
         if(temp==0)
         {
             finaly[i] = true;
             t = match[i];
             match[i] = p;
             if(t==0 || DFS(t))  return true;
             match[i] = t;
         }
         else if(slack[i]>temp)
         {
             slack[i] = temp;
         }
     }
     return false;
}
int KM()
{
    int i,j;
    memset(ly,0,sizeof(ly));
    memset(match,0,sizeof(match));
    for(i=1;i<=mcount;++i)
    {
        lx[i]=-maxData;
        for(j=1;j<=hcount;++j)
        {
            if(lx[i]<w[i][j])
                lx[i] = w[i][j];
        }
    }
    for(i=1;i<=mcount;++i)
    {
        for(j=1;j<=hcount;++j) slack[j] = maxData;
        while(1)
        {
            memset(finalx,0,sizeof(finalx));
            memset(finaly,0,sizeof(finaly));
            if(DFS(i)) break;
            int d = maxData;
            for(j=1;j<=hcount;++j)
            {
                if(!finaly[j] && d>slack[j])
                   d = slack[j];
            }
            for(j=1;j<=mcount;++j)
            {
                if(finalx[j])  lx[j]-= d;
                if(finaly[j])  ly[j]+= d;
                else  slack[j] -= d;                   
            }
        }
    } 
    int ans= 0;
    for(i=1;i<=mcount;++i)
    {
        ans -=(lx[i]+ly[i]);   //结果加的权值需要取反 
    }   
    return ans;
}
int main()
{
    //freopen("1.txt","r",stdin);
    int i,j;
    int n,m;
    string tempstr;
    while(cin>>n>>m)
    {
        if(n==0 && m==0)
            break;
        hcount = 0;
        mcount = 0;
        for(i=1;i<=n;++i)
        {
            cin>>tempstr;
            for(j=0;j<tempstr.size();++j)            
            {
                if(tempstr[j]=='H')
                {
                    hcount++;
                    houses[hcount].row = i;
                    houses[hcount].col = j+1;   
                }
                else if(tempstr[j]=='m')
                {
                    mcount++;
                    mans[mcount].row = i;
                    mans[mcount].col = j+1;
                }
            }      
        }
        for(i=1;i<=mcount;++i)
        {
            for(j=1;j<=hcount;++j)
            {
                //KM算法求的是最大权的完美匹配,所以将权值取反,权值是距离 
                w[i][j] = -(abs(mans[i].row-houses[j].row)+abs(mans[i].col-houses[j].col)); 
            }
        }
        cout<<KM()<<endl;
    }
    //getch();
    return 0;
}
//使用最小费用最大流实现 
//本题的关键在于建图:将小人和房间都作为图中的点,并设置一超级源点和超级终点,小人和房间的容量设置为1,费用设置为距离。源点到小人
//的容量设置为1,费用为0;房间到终点的容量设置为1,费用设置为0 
#include <iostream>
#include <string>
//#include <conio.h>
#include <queue>
#include <math.h>
using namespace std;
#define arraysize 205
typedef struct house
{
    int row;
    int col;
}house;
typedef struct littleman
{
    int row;
    int col;
}littleman;
house houses[101];
littleman mans[101];
int n,m; 
int mcount,hcount;
int maxData = 10000000;
int capacity[arraysize][arraysize];
int kcost[arraysize][arraysize];
bool final[arraysize];       //SPFA算法中标识结点是否在队列中 
int d[arraysize];
int flow[arraysize][arraysize];
int pre[arraysize];
int ncount;       
void SPFA(int src)
{
    queue<int> myqueue;
    int i,j;
    memset(final,0,sizeof(final));
    memset(pre,-1,sizeof(pre));
    myqueue.push(src);
    for(i=0;i<=ncount;++i)      //SPFA算法与Dij不同,SPFA初始化时将除源点以外所有点的最短距离初始化无穷大,ncount为结点总数
    {
        d[i] = maxData;
    }
    d[src] = 0;        //源点最短距离设置成0 
    final[src] = true;
    while(!myqueue.empty())     //SPFA算法可入队列多次 
    {
        int frontint = myqueue.front();myqueue.pop();
        final[frontint] = false;
        for(i=0;i<=ncount;++i)
        {
            if(capacity[frontint][i]>flow[frontint][i] && d[i]>d[frontint]+kcost[frontint][i])     //c[u][v]>f[u][v]说明<u,v>之间流量可以进行增加 
            {
                d[i] = d[frontint]+kcost[frontint][i];
                pre[i] = frontint;             //修改前驱 
                if(!final[i])
                {
                    final[i] = true;
                    myqueue.push(i);
                }
            }
        }
    }
}
void minCost(int src,int des)
{
    int minAdd = maxData;
    int p;
    while(1)
    {
        SPFA(src);
        if(pre[des]==-1)           //表示已无增广路
            break;
        minAdd = maxData;
        p = des;
        while(pre[p]!=-1)
        {
            minAdd = min(minAdd,(capacity[pre[p]][p]-flow[pre[p]][p])); //求的增广路的可增流量 
            p = pre[p];
        }
        p = des;
        while(pre[p]!=-1)         //沿着最小费用路进行增广 
        {
            flow[pre[p]][p] += minAdd;
            flow[p][pre[p]] -= minAdd;
            p = pre[p];
        }    
    }    
}
int main()
{
    //freopen("1.txt","r",stdin);
    int i,j;
    int src,des;
    int flowcost;
    string tempstr;
    while(cin>>n>>m)
    {
        if(n==0 && m==0)
            break; 
        flowcost = 0;   //最小费用 
        mcount = 0;  //小人的数目 
        hcount = 0;  //房间的数目 
        memset(kcost,0,sizeof(kcost)); 
        memset(capacity,0,sizeof(capacity));  
        memset(flow,0,sizeof(flow));   
        for(i=1;i<=n;++i)
        {
            cin>>tempstr;
            for(j=0;j<tempstr.size();++j)
            {
                if(tempstr[j]=='H')
                {
                     hcount++;
                     houses[hcount].row = i;
                     houses[hcount].col = j+1;             
                }
                else if(tempstr[j]=='m')
                {
                     mcount++;
                     mans[mcount].row = i;
                     mans[mcount].col = j+1;
                }
            }    
        }
        //小人和房间之间构图 
        for(i=1;i<=mcount;++i)
        {
            for(j=1;j<=hcount;++j)
            {
                kcost[i][j+mcount] = abs(mans[i].row-houses[j].row)+abs(mans[i].col-houses[j].col); //费用设置为距离 
                kcost[j+mcount][i] = -kcost[i][j+mcount];          
                capacity[i][j+mcount] = 1;                         
            }
        }
        src = 0;
        des = hcount+mcount+1; 
        ncount = hcount+mcount+2;     //结点总数
        //源点到小人之间构图 
        for(i=1;i<=mcount;++i)
        {
            capacity[src][i] = 1;
        }
        //房间到终点之间构图 
        for(j=1;j<=hcount;++j)
        {
            capacity[j+mcount][des] = 1;
        }
        minCost(src,des);
        for(i=1;i<=mcount;++i)
        {
            for(j=1;j<=hcount;++j)
            {
                flowcost += kcost[i][j+mcount]*flow[i][j+mcount]; //最小费用 
            }
        } 
        cout<<flowcost<<endl;        
    }
    //getch();
    return 0;
}
                    
                
                
            
        
浙公网安备 33010602011771号