KM算法入门

摘要:
*/#include#include#include#include//裸模板..constntmaxn=101;组成INF=-1;intw[maxn][maxn];intlx[maxn],ly[maxn';//顶部标记intlinky[maxn];intvisx[maxn],visy[maxn]];intslack[maxn];intnx,ny;Boolfind{visx[x]=true;对于{ifcontinue;int=lx[x]+ly[y]-w[x][y];如果{visy[y]=true,如果{link[y]=x;returntrue;//查找扩展轨迹}}elseifslack[y]=t;}returnfalse;//找不到扩充轨道}intKM()//最佳匹配值{inti,j;memset;memset;forforiflx[i]=w[i][j];for{forlick[i]=INF;而{memset;memset;if//找到扩充磁道并退出中断;intd=INF;for//未找到。调整l并找到{if(!Vision[i]&&d>slip[i])d=slip[i;}对于{iflx[i]-=d;}对于{ifly[i]+=d;elseslack[i]-=d;}}}intersult=0;对于ifresult+=w[linky[i]][i];returnresult;}intmain(){//freopen;而{scanf;inta,b,c;而{w[a][b]=c;}printf;中断;}return0;}话题推荐:第一题:hdu2255,奔小康,赚大钱http://acm.hdu.edu.cn/showproblem.php?pid=2255该模板未解释ViewCode问题2:hdu1533PointHomehttp://acm.hdu.edu.cn/showproblem.php?pid=1533使用w[i][j]=-w[i][j]构建一个映射,然后设置一个模板来计算输出ViewCode Item 3:hdu1853CyclicTour的最大值注意,这是一个有向图,并判断多个边ViewCode Item 4:hdu3488Tourhttp://acm.hdu.edu.cn/showproblem.php?pid=3435代码与第三个问题的代码基本相同。它只需要双向映射。福达的Aekdy Coin也有问题,这是一个危险的问题。时间

KM算法的基本概念:

http://baike.baidu.com/view/739278.htm

http://baike.baidu.com/view/501092.htm

看这个算法之前,最好先看下匈牙利算法,KM算法 是建立在匈牙利算法基础上实现的

对于这个算法最有误区的地方,个人感觉还是在  X 集合 -d  和 Y 集合 + d之后 还要进行

操作,再加上 深搜递归操作  ,理解容易产生误区,在这里我给出一组模板的测试数据来帮助初学者理解

注意观察: visx[],visy[],lx[],ly[],linky[],在调用中的变化:

3 4

0 0 2

0 1 6

1  1 7

2 1  14

2  2  3

模板:(O  ^ 4) 

复制代码
#define M 505
#define inf 0x3fffffff
bool sx[M], sy[M];
int match[M], w[M][M], n, m, d, lx[M], ly[M];
//n:左集元素个数; m:右集元素个数
void init ()
{
memset (w, 0, sizeof(w)); //不一定要,求最小值一般要初始化为负无穷!
}

bool dfs (int u)
{
int v; sx[u] = true;
for (v = 0; v < m; v++)
{
if (!sy[v] && lx[u]+ly[v]==w[u][v])
{
sy[v] = true;
if (match[v] == -1 || dfs (match[v]))
{
match[v] = u;
return true;
}
}
}
return false;
}

int KM ()
{
int i, j, k, sum = 0;
memset (ly, 0, sizeof(ly));
for (i = 0; i < n; i++)
{
lx[i] = -inf;
for (j = 0; j < m; j++)
if (lx[i] < w[i][j])
lx[i] = w[i][j];
}
memset (match, -1, sizeof(match));
for (i = 0; i < n; i++)
{
while (1)
{
memset (sx, false, sizeof(sx));
memset (sy, false, sizeof(sy));
if (dfs (i))
break;
d = inf;
for (j = 0; j < n; j++)
if (sx[j])
for (k = 0; k < m; k++)
if (!sy[k])
d = min (d, lx[j]+ly[k]-w[j][k]);
if (d == inf) //找不到完美匹配
return -1;
for (j = 0; j < n; j++)
if (sx[j])
lx[j] -= d;
for (j = 0; j < m; j++)
if (sy[j])
ly[j] += d;
}
}
for (i = 0; i < m; i++)
if (match[i] > -1)
sum += w[match[i]][i];
return sum;
}
复制代码

改进后的模板(O^3)

复制代码
/*其实在求最大 最小的时候只要用一个模板就行了,把边的权值去相反数即可得到另外一个.求结果的时候再去相反数即可*/
/*最大最小有一些地方不同。。*/
#include <iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
//赤裸裸的模板啊。。
const int maxn = 101;
const int INF = (1<<31)-1;
int w[maxn][maxn];
int lx[maxn],ly[maxn]; //顶标
int linky[maxn];
int visx[maxn],visy[maxn];
int slack[maxn];
int nx,ny;
bool find(int x)
{
visx[x] = true;
for(int y = 0; y < ny; y++)
{
if(visy[y])
continue;
int t = lx[x] + ly[y] - w[x][y];
if(t==0)
{
visy[y] = true;
if(linky[y]==-1 || find(linky[y]))
{
linky[y] = x;
return true; //找到增广轨
}
}
else if(slack[y] > t)
slack[y] = t;
}
return false; //没有找到增广轨(说明顶点x没有对应的匹配,与完备匹配(相等子图的完备匹配)不符)
}

int KM() //返回最优匹配的值
{
int i,j;

memset(linky,-1,sizeof(linky));
memset(ly,0,sizeof(ly));
for(i = 0; i < nx; i++)
for(j = 0,lx[i] = -INF; j < ny; j++)
if(w[i][j] > lx[i])
lx[i] = w[i][j];
for(int x = 0; x < nx; x++)
{
for(i = 0; i < ny; i++)
slack[i] = INF;
while(true)
{
memset(visx,0,sizeof(visx));
memset(visy,0,sizeof(visy));
if(find(x)) //找到增广轨,退出
break;
int d = INF;
for(i = 0; i < ny; i++) //没找到,对l做调整(这会增加相等子图的边),重新找
{
if(!visy[i] && d > slack[i])
d = slack[i];
}
for(i = 0; i < nx; i++)
{
if(visx[i])
lx[i] -= d;
}
for(i = 0; i < ny; i++)
{
if(visy[i])
ly[i] += d;
else
slack[i] -= d;
}
}
}
int result = 0;
for(i = 0; i < ny; i++)
if(linky[i]>-1)
result += w[linky[i]][i];
return result;
}

int main()
{
// freopen("g:/1.txt","r",stdin);
while(true)
{
scanf("%d%d",&nx,&ny);
int a,b,c;
while(scanf("%d%d%d",&a,&b,&c),a+b+c)
{
w[a][b]=c;
}
printf("%d ",KM());
break;
}
return 0;
}
复制代码

题目推荐:

第一题:hdu  2255   奔小康赚大钱

http://acm.hdu.edu.cn/showproblem.php?pid=2255

模板不解释

KM算法入门第5张View Code

第二题:  hdu 1533  Going Home

http://acm.hdu.edu.cn/showproblem.php?pid=1533

 用 w[i][j] = -w[i][j]建图再套模板 求最大值   输出【-sum】

KM算法入门第6张View Code

第三题:  hdu  1853  Cyclic Tour

注意是有向图,和重边的判断

KM算法入门第7张View Code


第四题:hdu  3488  Tour

http://acm.hdu.edu.cn/showproblem.php?pid=3488 
跟第三题几乎一样

KM算法入门第8张View Code

第五题:hdu  3435  A new Graph Game

http://acm.hdu.edu.cn/showproblem.php?pid=3435 
跟第三题代码基本上一样,只是要双向建图,也有重边

福大 AekdyCoin 出的题,好险的题啊,时间跑了 2000+ ;

KM算法入门第9张View Code

第六题: hdu 2426  Interesting Housing Problem

http://acm.hdu.edu.cn/showproblem.php?pid=2426

注意 题目输入 |Vi| <= 10000  
左集是学生,右集是房子,w[i][j] < 0 不可匹配,最后无法完美匹配输出-1

KM算法入门第10张View Code

第七题:hdu 2853  Assignment

http://acm.hdu.edu.cn/showproblem.php?pid=2853 
思路:让原有匹配更有优势就可以了 
实现:所有权值扩大100倍,原有匹配【例如a匹配b】w[a][b]+ + 
设结果是res 
最大值:res/100 
至少改变个数:n - res%100  

这种处理比较有意思

KM算法入门第11张View Code

第八题:hdu 3718  

http://acm.hdu.edu.cn/showproblem.php?pid=3718 
题目求的是两字符串的最大相似度 
思路:因为第一个串的一种字母只能匹配第二个串的一种字母,所以可以转化为求 
【字母的最大匹配值/n】 注意输入 scanf--%s   不要用 %c%*c 

KM算法入门第12张View Code

推荐题目链接:

http://972169909-qq-com.iteye.com/blog/1184514

免责声明:文章转载自《KM算法入门》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇最新Dreamweaver 8.0序列号 Dreamweaver 8.0注册码chrome显示小于12号字体的方法下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

随便看看

powerdesigner与数据库之间的连接

useUnicode=true&characterEncoding=UT8&serverTimezone=UTC 9JDBCdriverjarfiles:指定连接的jar包路径。测试连接。连接成功。进入工作区。3.2 3.2powerdesigner连接到oracle。其原理与连接MySQL的原理相同。在已安装的oracle下找到ojdbc1...

关于利用RD client远程电脑,和输入法的一些问题

我在寝室,利用转接头,借助手机app“RDclient”成功完成在键盘上输入文字,并在电脑上输出内容。“RDclient”可以完成手机端远程连接电脑端,只需要知道电脑的ip,用户名和密码就可以。“RDclient”这是微软自家发布的软件,还是很不错的。不过,在刚开始连接的时候,输入法遇到一点问题。后来,在手机上,调出输入法打字的界面,更换成英文输入模式,然后...

使用AutoHotKey提升工作效率

打开网站并按TAB键,直到到达输入字段并计算点击次数。使用以下代码将“名字”、“中间名”、“姓氏”和其他两个ID放入Web表单。...

【资料】2021年最网红的FPGA开发板之一——DE10-Nano (SOC FPGA入门推荐!)

DE10 Nano开发板是2021最受欢迎的FPGA开发板之一。除了广泛应用于物联网、边缘计算、硬件加速、AI和EDA教育课程之外,许多爱好者还在网络上日益流行的开源复古游戏项目Mister中使用它。让我们来看看DE10 Nano提供的材料:Youjing官方网站上的材料(中文手册可用!!!23~课程培训材料2018产学合作培训材料基于2018产学协作培训材...

JS前端数据多条件筛选(商品搜索)

有时候也会需要在前端进行数据筛选,增强交互体验。当数据可用的筛选条件较多时,把逻辑写死会给后期维护带来很大麻烦。进行商品按条件筛选主要是利用Arrary.prototype.filter对数组元素进行遍历检查,返回一个符合检查条件的新数组,不会改变原数组。为了能自动适配不同的筛选条件,将筛选条件分为两个大类,一个是区间类型rangesFilter,如:品牌、...

JavaMail给QQ邮箱发邮件报错

org.springframework.mail.MailAuthenticationException:身份验证失败;nestedexceptionisjavax.mail.AuthenticationFailedException:535错误:http://service.mail.qq.com/cgi-bin/help?subtype=1&&a...