此篇博客为转载并处理过的,希望能帮助你,如遇不足,请多见谅并指教:
希望你在阅读此篇博客时,你已经学会了匈牙利算法,这样会对你更有帮助哦!!!!!
对于KM算法自己的通俗理解与代码详解:
注:KM算法:就是在匈牙利基础上加了 权值 的束缚!
那么,为了达到权值和最大 ,或者 最小,就不能简单的去算最多的边数。
步骤:(以HDU2255 例题为例)
1.首先要找到所有居民愿意花钱最多的 那个房子。
题目中用到lx,ly数组,是为了同时调节两个数组,使得权值和最大。
或者说当要松弛的时候使得 本来最大的矛盾权值和 尽可能的损失小一些来得到 满足条件的最大权值和!
2.(lx[x]+ly[y]-w[x][y]=0)条件下进行匈牙利算法。
lx[x]+ly[y]-w[x][y]这个条件十分巧妙:(算法精髓)
1.即可以只让指定居民 找到 它愿意付最多钱的房子。
2.又可以在发生多居民抢一个房子时,用它来得到该居民到其它房子的松弛量!(即该居民到其它房子 比 到这个用钱最多的房子 愿意花的钱数上差的值。)
那么我们就要把 发生矛盾的居民到 其它房子的松弛量 的最小值求出来。再用它去松弛,就可以让原本矛盾的最大权值和,损失最小而得到满足条件的最大权值和
对于每个居民有4个基本问题:1.这个房子访问过没有?
2.这个房子能不能满足他的条件
3.这个房子是否被别人住了
4.被别人住了能不能得到调配
代码如下:
#include <iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define MAX 310
#define INF 1<<25
#define clr(x) memset(x,0,sizeof(x))
int w[MAX][MAX];
int n;
int lx[MAX],ly[MAX]; /***lx[i]初始化为A集合中 i 点能到B集合某一点的最大权值, ly[i] 初始化0;***/
int link[MAX];
int slack[MAX];
int visx[MAX],visy[MAX];
bool find(int x)
{
visx[x]=1; /****得到发生矛盾的居民集合****/
for(int y=1;y<=n;y++) /**这个居民,每个房子都去试一试!(找到就退出)**/
{
if(visy[y]) /****一个房子不需要重复访问****/
continue;
int t=lx[x]+ly[y]-w[x][y];/****按这个标准去用-匈牙利算法***/
if(t==0) /**t==0标志这个房子可以给这位居民**/
{
visy[y]=1;
if(link[y]==0||find(link[y])) /****这房子没人住 或 可以让住这个房子的人去找另外的房子住****/
{
link[y]=x;
return true; /**那么就可以让这位居民住进来**/
}
}
else if(slack[y]>t) /**否则这个房子不能给这位居民!**/
slack[y]=t; /***就要找到这个房子要松弛多少才能够给这位居民***/
/***且当有多个居民都对这个房子有松弛量时,要找到最小的。****/
}
return false; /***一定不能忘记哦!***/
}
int KM()
{
clr(lx);
clr(ly);
clr(link);
/*****首先把每个居民出的钱最多的那个房子赋给它******/
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];
/*****n循环-在满足了以前居民的情况下-给第i个居民安置房子*****/
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
slack[j]=INF; /***松弛量***//****这个松弛量不需要每次dfs都初始化一次;因为它跟visy有关***/
while(1) /****死循环-在于一定要给这个居民找到房子为止!****/
{
clr(visx);
clr(visy);
if(find(i)) /***找到房子就跳出死循环***/
break;
int d=INF;
for(int k=1;k<=n;k++)
if(!visy[k]&&d>slack[k])
d=slack[k]; /****找到最小松弛量*****/
for(int k=1;k<=n;k++) /****松弛操作-使发生矛盾的居民有更多选择*****/
{
if(visx[k]) /*****将矛盾居民的要求降低,使他们有更多房子可选择*****/
lx[k]-=d;
if(visy[k]) /****使发生矛盾的房子在下一个子图,保持矛盾(即保持原子图性质)*****/
ly[k]+=d;
}
}
}
int ans=0;
for(int i=1;i<=n;i++)
ans+=w[link[i]][i];
return ans;
}
int main()
{
while(~scanf("%d",&n))
{
clr(w); //每个案列都要重新置0;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
scanf("%d",&w[i][j]); //输入每边权值
printf("%d\n",KM());
}
return 0;
}
一起学习,一起进步…..