重做 poj 2195(KM算法)

//KM算法求解最小权,二分最优匹配
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<vector>
#include<cmath>
using namespace std;
#define INF 99999999
#define maxn 105

char str[maxn][maxn];//存输入的字符数组
int lx[maxn],ly[maxn];//顶标
int xMatch[maxn],yMatch[maxn];//记录x与y的匹配值
bool visx[maxn],visy[maxn];
int w[maxn][maxn];//权值
int row,col;
int ans,slack,n;//slack为修改量
struct node
{
    int x,y;
    node(int xx,int yy)
    {
        x=xx;
        y=yy;
    }
};
bool findPath(int x)//寻找最优解
{
    int temp;
    visx[x]=true;
    for(int y=1; y<=n; y++)
    {
        if(visy[y])continue;
        temp=w[x][y]-lx[x]-ly[y];
        if(temp==0)//说明是相等子图
        {
            visy[y]=true;
            if(!yMatch[y]||findPath(yMatch[y]))
            {
                xMatch[x]=y;
                yMatch[y]=x;
                return true;
            }
        }
        else if(slack>temp)//更新slack的值(最小)
            slack=temp;
    }
    return false;
}
void km()
{
    memset(xMatch,0,sizeof(xMatch));
    memset(yMatch,0,sizeof(yMatch));
    memset(ly,0,sizeof(lx));//初始化顶标
    for(int i=0; i<=n; i++)
        lx[i]=INF;
    for(int i=1; i<=n; i++)
        for(int j=1; j<=n; j++)
            if(lx[i]>w[i][j])
                lx[i]=w[i][j];//
    for(int x=1; x<=n; x++)
    {
        while(1)
        {
            memset(visx,0,sizeof(visx));
            memset(visy,0,sizeof(visy));
            slack=INF;
            if(findPath(x))break;
            for(int i=1; i<=n; i++)
            {
                if(visx[i]) lx[i]+=slack;//若求最大权匹配改为lx[i]-=slack;
                if(visy[i]) ly[i]-=slack;//ly[i]+=slack;
            }
        }
    }
}
int main()
{
    while(scanf("%d%d",&row,&col))
    {
        if(row==0&&0==col)break;
        ans=n=0;
        vector<node> h,m;
        for(int i=0; i<row; i++)
        {
            cin>>str[i];
            for(int j=0; j<col; j++)
            {
                if(str[i][j]=='m')
                {
                    m.push_back(node(i,j));//容器记录坐标(人的坐标)
                    ++n;
                }
                if(str[i][j]=='H')
                    h.push_back(node(i,j));//记录房子的坐标
            }
        }
        for(int i=0;i<m.size();i++)
        for(int j=0;j<h.size();j++)
        w[i+1][j+1]=abs(double(m[i].x-h[j].x))+abs(double(m[i].y-h[j].y));//计算距离。
        km();
        for(int y=1;y<=n;y++)
        ans+=w[yMatch[y]][y];
        cout<<ans<<endl;
    }
    return 0;
}


 

你可能感兴趣的:(重做 poj 2195(KM算法))