如果,将求二分图的最大匹配的所有匹配边的权重看做1

那么用匈牙利算法求二分图的最大匹配的问题也可以看成求二分图的最大权匹配

如果边权是特例,我们就要使用KM算法来做了

这个算法其实还是比较难的,会用就不错了,更不要说证明了

这里以HDU2255为例,这是一个裸题

在这个题目里面X和Y的size是一样的

然后我们稍微介绍一下这个算法(详细的以后再说吧,目前能力不够)

int n,nx,ny,ans;
int linker[maxn],lx[maxn],ly[maxn],slack[maxn],visx[maxn],visy[maxn];
int G[maxn][maxn];

linker记录的是与当前的下标节点(Y中)相连的X节点,lx和ly是节点顶标,slack是Y定点的松弛量函数

邻接矩阵存储

在这里面,如果有的边不存在,设置权重为0,这样图就可以近似看成一个全连接二分图

    for(int i=;i<=nx;i++)
{
lx[i]=-INF;
for(int j=;j<=ny;j++)
{
if(G[i][j]>lx[i]) lx[i]=G[i][j];
}
}

首先初始化X中节点的节点顶标

就是对于每一个节点,看其所连接的所有的边,将最大权重设置为X节点顶标

然后呢,就是从每个节点开始进行DFS增广

根据情况修改可行顶标

    for(int x=;x<=nx;x++)
{
for(int i=;i<=ny;i++) slack[i]=INF;
while()
{
memset(visx,,sizeof(visx));
memset(visy,,sizeof(visy));
if(dfs(x)) break; //找到增广路,进入下一个点的增广
//如果失败,需要改变顶标使图中可行边数量增加
//在所有的增广路的x顶标中减去常数d
//在所有增广路的Y顶标中增加一个常数d
int d=INF;
for(int i=;i<=ny;i++)
if(!visy[i]&&d>slack[i])
d=slack[i];
for(int i=;i<=nx;i++)
if(visx[i]) lx[i]-=d;
for(int i=;i<=ny;i++)
if(visy[i]) ly[i]+=d;
else slack[i]-=d;
}
}

然后DFS增广部分如下:

int dfs(int x)
{
visx[x]=;
for(int y=;y<=ny;y++)
{
if(visy[y]) continue;
int tmp=lx[x]+ly[y]-G[x][y];
if(tmp==)
{
visy[y]=;
if(linker[y]==-||dfs(linker[y]))
{linker[y]=x;return ;}
}
else if(slack[y]>tmp) slack[y]=tmp;
}
return ;
}

具体原理先鸽了,以后再说

然后给出完整的实现:

 #include<cstdio>
#include<cstring>
using namespace std;
const int INF=;
const int maxn=;
int n,nx,ny,ans;
int linker[maxn],lx[maxn],ly[maxn],slack[maxn],visx[maxn],visy[maxn];
int G[maxn][maxn];
int dfs(int x)
{
visx[x]=;
for(int y=;y<=ny;y++)
{
if(visy[y]) continue;
int tmp=lx[x]+ly[y]-G[x][y];
if(tmp==)
{
visy[y]=;
if(linker[y]==-||dfs(linker[y]))
{linker[y]=x;return ;}
}
else if(slack[y]>tmp) slack[y]=tmp;
}
return ;
}
int KM()
{
memset(linker,-,sizeof(linker));
memset(ly,,sizeof(ly));
for(int i=;i<=nx;i++)
{
lx[i]=-INF;
for(int j=;j<=ny;j++)
{
if(G[i][j]>lx[i]) lx[i]=G[i][j];
}
}
for(int x=;x<=nx;x++)
{
for(int i=;i<=ny;i++) slack[i]=INF;
while()
{
memset(visx,,sizeof(visx));
memset(visy,,sizeof(visy));
if(dfs(x)) break; //找到增广路,进入下一个点的增广
//如果失败,需要改变顶标使图中可行边数量增加
//在所有的增广路的x顶标中减去常数d
//在所有增广路的Y顶标中增加一个常数d
int d=INF;
for(int i=;i<=ny;i++)
if(!visy[i]&&d>slack[i])
d=slack[i];
for(int i=;i<=nx;i++)
if(visx[i]) lx[i]-=d;
for(int i=;i<=ny;i++)
if(visy[i]) ly[i]+=d;
else slack[i]-=d;
}
}
int res=;
for(int i=;i<=ny;i++)
if(linker[i]!=-)
res+=G[linker[i]][i];
return res; }
int main()
{
while(scanf("%d",&n)==)
{
nx=ny=n;
for(int i=;i<=n;i++)
for(int j=;j<=n;j++)
scanf("%d",&G[i][j]);
ans=KM();
printf("%d\n",ans);
}
return ;
}

图论:KM算法的更多相关文章

  1. 图论(KM算法):COGS 290. [CTSC2008] 丘比特的烦恼

    290. [CTSC2008] 丘比特的烦恼 ★★★   输入文件:cupid.in   输出文件:cupid.out   简单对比 时间限制:1 s   内存限制:128 MB 随着社会的不断发展, ...

  2. 图论补档——KM算法+稳定婚姻问题

    突然发现考前复习图论的时候直接把 KM 和 稳定婚姻 给跳了--emmm 结果现在刷训练指南就疯狂补档.QAQ. KM算法--二分图最大带权匹配 提出问题 (不严谨定义,理解即可) 二分图 定义:将点 ...

  3. 图论(二分图,KM算法):HDU 3488 Tour

    Tour Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others)Total Submis ...

  4. 【HDU 2255】奔小康赚大钱 (最佳二分匹配KM算法)

    奔小康赚大钱 Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Subm ...

  5. 【原创】我的KM算法详解

    0.二分图 二分图的概念 二分图又称作二部图,是图论中的一种特殊模型. 设G=(V, E)是一个无向图.如果顶点集V可分割为两个互不相交的子集X和Y,并且图中每条边连接的两个顶点一个在X中,另一个在Y ...

  6. KM算法详解[转]

    KM算法详解 原帖链接:http://www.cnblogs.com/zpfbuaa/p/7218607.html#_label0 阅读目录 二分图博客推荐 匈牙利算法步骤 匈牙利算法博客推荐 KM算 ...

  7. 训练指南 UVALive - 4043(二分图匹配 + KM算法)

    layout: post title: 训练指南 UVALive - 4043(二分图匹配 + KM算法) author: "luowentaoaa" catalog: true ...

  8. 图论常用算法之一 POJ图论题集【转载】

    POJ图论分类[转] 一个很不错的图论分类,非常感谢原版的作者!!!在这里分享给大家,爱好图论的ACMer不寂寞了... (很抱歉没有找到此题集整理的原创作者,感谢知情的朋友给个原创链接) POJ:h ...

  9. 【POJ 2195】 Going Home(KM算法求最小权匹配)

    [POJ 2195] Going Home(KM算法求最小权匹配) Going Home Time Limit: 1000MS   Memory Limit: 65536K Total Submiss ...

随机推荐

  1. Android开发——告诉你Adapter应该写在Activity里面还是外面

    0. 前言 本文转载自AItsuki的博客. 首先说明一下为什么要写这么一篇博客:最近看了一些其他人的项目,发现很多项目的做法是建立一个专门存放Adapter类的Package包,也有的项目干脆直接都 ...

  2. 通过修改Host文件解决主机头访问网站的问题

             网站打包发布后,一般都是通过IP地址来进行访问,但是这样不方便记忆.如何设置一个简单的域名,然后通过域名来进行访问呢?一个可行的方法就是修改本机的host文件,添加一条映射关系,把这 ...

  3. Android开发免费类库和工具集合

    用于Android开发的免费类库和工具集合,按目录分类. Action Bars ActionBarSherlock Extended ActionBar FadingActionBar GlassA ...

  4. 玩转VIM-札记(三)

    玩转VIM-札记(三) 眨眼之间,5月就要从指间溜走,不给人一点点遐想的时间,我要赶紧抓着五月的尾巴,在博客中在添一笔.那么就还接着Vim来说吧.以Vim来为五月画上一个句号. 返璞归真 相信经过玩转 ...

  5. Git 上传本地仓库到码云

    一.将本地的项目上传到码云 1.码云上创建一个项目 testgit (名字随你) 2.本地创建一个文件夹D:/testgit,然后使用git bash 3.cd 到本地文件夹中D:/testgit 4 ...

  6. C++学习014函数值传递和地址传递

    当我们给一个函数传参数的时候,可以直接值传入函数,也给可以把一个地址传入函数 区别就是一个本身不被改变,而另一本身也在改变, 在开发时候都会用到, 这里做下记录 #include <iostre ...

  7. 【Swift】日期比较函数 记录下 Comparing date in Swift

    Add this code to your project and comparing dates is easier than ever 扩展NSDATE //swift 3.0.2 extensi ...

  8. OpenCV实现SIFT图像拼接源代码

    OpenCV实现SIFT和KDtree和RANSAC图像拼接源代码,此源代码由Opencv2.4.13.6和VC++实现,代码本人已经调试过,完美运行,效果如附图.Opencv2.4.13.6下载地址 ...

  9. C++ 学习笔记之——文件操作和文件流

    1. 文件的概念 对于用户来说,常用到的文件有两大类:程序文件和数据文件.而根据文件中数据的组织方式,则可以将文件分为 ASCII 文件和二进制文件. ASCII 文件,又称字符文件或者文本文件,它的 ...

  10. K-Means和FCM聚类

    K均值聚类是基于原型的.划分的聚类方法.聚类数K由用户指定,初始的K个聚类中心随机选取,然后将每个点分派到最近的聚类中心,形成K个簇,接下来重新计算每个簇的聚类中心,重复上一步,直到簇不发生变化或达到 ...