带权二分图匹配KM算法

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int INF=1e9;
int flag;
struct zp
{
    int x,y;
}man[200],house[200];
int x[200][200],mn,ho,lx[200],ly[200];
void build_map()//建图,单向图
{
    for(int i=0; i<mn; i++)
    {
        for(int j=0; j<ho; j++)
        {
            x[i+1][j+1]=abs(man[i].x-house[j].x)+abs(man[i].y-house[j].y);
        }
    }
}
int visx[200],visy[200],dis[200],ans;
int dfspath(int k)//匈牙利算法,寻找路径
{
    visx[k]=1;
    for(int i=1; i<=mn; i++)
    {
        if(!visy[i]&&lx[k]+ly[i]==x[k][i])
        {
            visy[i]=1;
            if(dis[i]==-1||dfspath(dis[i]))
            {
                dis[i]=k;
                return 1;
            }
        }
    }
    return 0;
}
int KM()
{
    if(!flag)//最小权值处理
    {
        for(int i=1;i<=mn;i++)
            for(int j=1;j<=ho;j++)
            x[i][j]=-x[i][j];
    }
    for(int i=1;i<=mn;i++)//初始化x点集标号
    {
        lx[i]=x[i][1];
        for(int j=2;j<=ho;j++)
            lx[i]=max(lx[i],x[i][j]);
    }
    memset(ly,0,sizeof(ly));//初始化y点集标号
    memset(dis,-1,sizeof(dis));//保存对应关系
    for(int i=1; i<=mn; i++)
    {
        while(1)
        {
            memset(visx,0,sizeof(visx));
            memset(visy,0,sizeof(visy));
            if(dfspath(i))//找到路径
                break;
                int Min=INF+1;
                for(int j=1;j<=mn;j++)//扩大子图范围
                {
                    if(visx[j])
                        for(int k=1;k<=ho;k++)
                        if(!visy[k])
                        Min=min(Min,lx[j]+ly[k]-x[j][k]);
                }
                for(int j=1;j<=mn;j++)//更新x点集标号
                    if(visx[j])
                    lx[j]-=Min;
                for(int j=1;j<=ho;j++)//更新y点集标号
                    if(visy[j])
                    ly[j]+=Min;
        }
    }
    ans=0;
    for(int i=1;i<=ho;i++)//计算权值和1
        ans+=x[dis[i]][i];
        if(!flag)//求最小权值
            ans=-ans;
        return ans;//返回权值
}
int main()
{
    int n,m;
    while(~scanf("%d%d",&n,&m)&&(n||m))
    {
        mn=ho=0;
        char a[200];
        memset(x,0,sizeof(x));
        //flag=1;//最大权匹配
        flag=0;//最小权匹配 ,把权值取反然后求最大权匹配然后把结果取反
        for(int i=0; i<n; i++)
        {
            scanf("%s",a);
            for(int j=0; j<m; j++)
            {
                if(a[j]=='m') man[mn].x=i+1,man[mn++].y=j+1;
                else if(a[j]=='H') house[ho].x=i+1,house[ho++].y=j+1;
            }
        }
        build_map();
        printf("%d\n",KM());
    }
}

上面题目链接
题目大概意思是让求所有的m到H走的所有路之和最小,每一个H只能承载1个m。
KM的一点优化,有两种方式
hdu 2255题目
一般解法:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int INF=1e9;
int flag;
int x[500][500],n,lx[500],ly[500];
int visx[500],visy[500],dis[500],ans;
int dfspath(int k)//匈牙利算法,寻找路径
{
    visx[k]=1;
    for(int i=0; i<n; i++)
    {
        if(!visy[i]&&lx[k]+ly[i]==x[k][i])
        {
            visy[i]=1;
            if(dis[i]==-1||dfspath(dis[i]))
            {
                dis[i]=k;
                return 1;
            }
        }
    }
    return 0;
}
int KM()
{
//    if(!flag)//最小权值处理
//    {
//        for(int i=0;i<=n;i++)
//            for(int j=0;j<n;j++)
//            x[i][j]=-x[i][j];
//    }
    memset(lx,0,sizeof(lx));
    memset(ly,0,sizeof(ly));//初始化y点集标号
    memset(dis,-1,sizeof(dis));//保存对应关系
    for(int i=0; i<n; i++) //初始化x点集标号
        for(int j=0; j<n; j++)
            lx[i]=max(lx[i],x[i][j]);
    for(int i=0; i<n; i++)
    {
        while(1)
        {
            memset(visx,0,sizeof(visx));
            memset(visy,0,sizeof(visy));
            if(dfspath(i))//找到路径
                break;
            int Min=INF+1;
            for(int j=0; j<n; j++) //扩大子图范围
            {
                if(visx[j])
                    for(int k=0; k<n; k++)
                        if(!visy[k])
                            Min=min(Min,lx[j]+ly[k]-x[j][k]);
            }
            for(int j=0; j<n; j++) //更新x,y点集标号
            {
                if(visx[j])
                    lx[j]-=Min;
                if(visy[j])
                    ly[j]+=Min;
            }
        }
    }
    ans=0;
    for(int i=0; i<n; i++) //计算权值和1
        ans+=x[dis[i]][i];
//        if(!flag)//求最小权值
//            ans=-ans;
    return ans;//返回权值
}
int main()
{
    while(~scanf("%d",&n))
    {
        //flag=1;//最大权匹配
        //flag=0;//最小权匹配 ,把权值取反然后求最大权匹配然后把结果取反
        for(int i=0; i<n; i++)
            for(int j=0; j<n; j++)
                scanf("%d",&x[i][j]);
        //build_map();
        printf("%d\n",KM());
    }
}

优化后的:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int INF=1e9;
int flag;
int x[500][500],n,lx[500],ly[500];
int visx[500],visy[500],dis[500],ans,slack[500],Min;
void build_map()//建图,单向图
{

}
int dfspath(int k)//匈牙利算法,寻找路径
{
    visx[k]=1;
    for(int i=0; i<n; i++)
    {
        if(visy[i])
            continue;
        int t=lx[k]+ly[i]-x[k][i];
        if(t==0)
        {
            visy[i]=1;
            if(dis[i]==-1||dfspath(dis[i]))
            {
                dis[i]=k;
                return 1;
            }
        }
        else
            //slack[i]=min(slack[i],t);//优化1
            Min=min(Min,t);//优化2
    }
    return 0;
}
int KM()
{
//    if(!flag)//最小权值处理
//    {
//        for(int i=0; i<n; i++)
//            for(int j=0; j<n; j++)
//                x[i][j]=-x[i][j];
//    }
    memset(lx,0,sizeof(lx));
    memset(ly,0,sizeof(ly));//初始化y点集标号
    memset(dis,-1,sizeof(dis));//保存对应关系
    for(int i=0; i<n; i++) //初始化x点集标号
        for(int j=0; j<n; j++)
            lx[i]=max(lx[i],x[i][j]);
    for(int i=0; i<n; i++)
    {
//        for(int j=0;j<=n;j++)//优化1
//            slack[j]=INF;
        while(1)
        {
            memset(visx,0,sizeof(visx));
            memset(visy,0,sizeof(visy));
            Min=INF+1;
            if(dfspath(i))//找到路径
                break;
//            for(int j=0; j<n; j++)//扩大子图范围  优化1 优化2不要这一步
//                if(!visy[j])
//                    Min=min(Min,slack[j]);
            for(int j=0; j<n; j++) //更新x点集标号
            {
                if(visx[j])
                    lx[j]-=Min;
                if(visy[j])
                    ly[j]+=Min;
//                else//有这一步会超时,但是有的人博客上有这一步,参考下
//                slack[i]-=Min;
            }
        }
    }
    ans=0;
    for(int i=0; i<n; i++) //计算权值和
        ans+=x[dis[i]][i];
//    if(!flag)//求最小权值
//        ans=-ans;
    return ans;//返回权值
}
int main()
{
    while(~scanf("%d",&n))
    {
        flag=1;
        for(int i=0; i<n; i++)
            for(int j=0; j<n; j++)
                scanf("%d",&x[i][j]);
        printf("%d\n",KM());
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值