重做 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;
}


 

發佈了99 篇原創文章 · 獲贊 3 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章