#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());
}
}