突然发现考前复习图论的时候直接把 KM 和 稳定婚姻 给跳了……emmm

结果现在刷训练指南就疯狂补档。QAQ。

KM算法——二分图最大带权匹配

提出问题

(不严谨定义,理解即可)

二分图 定义:将点集 \(V\) 划分成两个不相交的集合 \(V_1,V_2\) (通常称为左右部点)使得不存在 \(u\in V_1,v\in V_2\) 且 \((u,v)\in E\) .

最大匹配 :给定一张二分图,求一个子图 \(G'\) ,称 \(G'\) 中的边为匹配边,原图 \(G\) 中的其他边为非匹配边,限制 \(G'\) 中每个点度数不超过 1,求匹配边的最大数量。

前置知识:匈牙利算法解决二分图最大匹配问题

定义 交替路 为:从一个未匹配点出发,依次经过非匹配边、匹配边交替形成的一条路径

定义 增广路 为:从一个未匹配点出发,以一个未匹配点结束的交替路。

很显然,当我们在原图中找出一条增广路时,非匹配边恰好比匹配边多一条,那么只要对这条路上匹配和非匹配情况取反,就可以使得匹配边数目增加1.重复这个过程,直到找不到增广路,得到的匹配就是最大匹配。

剩余流程和代码见 这里 (该链接已经指向匈牙利算法部分)

完备匹配 :左右部点个数均为 \(n\) 且最大匹配包含 \(n\) 条边。

带权匹配 :二分图中每条边有权值,在最大匹配的基础 上最大化这个边集的 权值和

分析问题

解决带权匹配一般有两种思路:

  • 费用流
  • KM算法(只适用于存在完备匹配的情况,在稠密图上效率较高。)

这里只讨论 KM 算法。

定义

顶标 :顾名思义,顶点的标记,存在于左右部点。为了方便叙述,左部点的顶标称为 \(a_i\) ,右部点的顶标称为 \(b_i\) ,顶标不唯一,可以调整。令左部点 \(i\) 和右部点 \(j\) 之间的边权为 \(w_{i,j}\) ,那么两个顶标满足 \(a_i+b_j=w_{i,j}\) .

相等边 : 满足 \(a_i+b_j=w_{i,j}\) 的边 \((i,j)\)

相等子图 :相等边构成的子图

交错树 :增广路径形成的树。考虑什么时候会匹配失败。发现我们当前求相等子图的完备匹配失败了,是因为对于某个左部点,我们找不到一条从它出发的增广路。那么这时候所有增广路形成了交错树,而且由于无法增广,叶子节点一定都是左部点。

结论

一个显然的结论:当每个相等子图完备匹配时,二分图得到最大匹配

流程

根据上面的定义,现在我们需要找到一个合适的顶标分配使得 “每个相等子图完备匹配”

具体步骤:

  1. 分配可行顶标。为了让 \(a_i+b_j\ge w_{i,j}\) 恒成立,令 \(a_i\) 为左部点 \(x_i\) 所有连边中的最大边权,\(b_j=0\) .

  2. 匈牙利算法找增广路。这部分就不在赘述。

  3. 找不到增广路就调整顶标。为了让更多的点进入相等子图,我们把交错树中左部点的顶标都减小某个值 \(del\) ,右部点的顶标都增加 \(del\) ,那么会发现:

    • 对于原本就在交错树中的边 \((i,j)\) ,\(a_i+b_j\) 不变,仍然在相等子图中。
    • 对于两端都不在交错树上的边 \((i,j)\) ,\(a_i,b_j\) 不变,仍然不在相等子图中。
    • 左端在交错树中,右端不在的边 \((i,j)\) ,\(a_i+b_j\) 减小,仍然不在相等子图中。
    • 左端不在交错树中,右端在的边 \((i,j)\) ,\(a_i+b_j\) 增大,有可能进入相等子图

    这样相等子图就得到了扩大。

  4. 重复 2、3 直到找到增广路

现在的问题就是如何求这个 \(del\) 了。为了让 \(a_i+b_j\ge w_{i,j}\) 始终成立,且至少有一条边进入相等子图,那么 \(del=\min\{a_i+b_j-s_{i,j}\}\) .

解决问题

优良模板题 花姐姐的数据用心了awa

DFS

一次只能找一条增广路,复杂度可以卡成 \(\mathcal{O}(n^4)\) .

据说这个东西能过 50 分……但是不知道是我常数大还是写假了,反正我只有 10pts,其余全 TLE。

这不重要,反正 BFS 的复杂度才是对的

//Author: RingweEH
const int N=510;
const ll inf=0x7f7f7f7f;
int n,m,match[N],visa[N],visb[N];
ll edge[N][N],del[N],vala[N],valb[N]; bool dfs( int u )
{
visa[u]=1;
for ( int v=1; v<=n; v++ )
if ( !visb[v] )
{
if ( vala[u]+valb[v]-edge[u][v]==0 )
{
visb[v]=1;
if ( !match[v] || dfs( match[v]) ) return match[v]=u,1;
else del[v]=min( del[v],vala[u]+valb[v]-edge[u][v] );
}
}
return 0;
} int KM()
{
memset( vala,-inf,sizeof(vala) );
for ( int u=1; u<=n; u++ )
for ( int v=1; v<=n; v++ )
vala[u]=max( vala[u],edge[u][v] );
for ( int u=1; u<=n; u++ )
{
while ( 1 )
{
memset( visa,0,sizeof(visa) ); memset( visb,0,sizeof(visb) );
memset( del,inf,sizeof(del) );
if ( dfs(u) ) break;
ll delta=inf;
for ( int v=1; v<=n; v++ )
if ( !visb[v] ) delta=min( delta,del[v] );
for ( int v=1; v<=n; v++ )
if ( visa[v] ) vala[v]-=delta;
for ( int v=1; v<=n; v++ )
if ( visb[v] ) valb[v]+=delta;
}
}
ll res=0;
for ( int v=1; v<=n; v++ )
res+=edge[match[v]][v];
return res;
}

BFS

换成 BFS 之后复杂度是 \(\mathcal{O}(n^3)\) .(因为 DFS 每次都是从头去找增广路……肯定慢啊)

//Author: RingweEH
const int N=510;
const ll inf=0x7f7f7f7f;
int n,m,match[N],visa[N],visb[N],pas[N];
ll edge[N][N],del[N],vala[N],valb[N],c[N]; void bfs( int u )
{
int a,v=0,vl=0,delta;
for ( int i=1; i<=n; i++ )
pas[i]=0,c[i]=inf;
match[v]=u;
do
{
a=match[v]; delta=inf; visb[v]=1;
for ( int b=1; b<=n; b++ )
if ( !visb[b] )
{
if ( c[b]>vala[a]+valb[b]-edge[a][b] )
c[b]=vala[a]+valb[b]-edge[a][b],pas[b]=v;
if ( c[b]<delta ) delta=c[b],vl=b;
}
for ( int b=0; b<=n; b++ )
if ( visb[b] ) vala[match[b]]-=delta,valb[b]+=delta;
else c[b]-=delta;
v=vl;
}while ( match[v] );
while ( v ) match[v]=match[pas[v]],v=pas[v];
} ll KM()
{
for ( int i=1; i<=n; i++ )
match[i]=vala[i]=valb[i]=0;
for ( int u=1; u<=n; u++ )
{
for ( int v=1; v<=n; v++ )
visb[v]=0;
bfs( u );
}
ll res=0;
for ( int u=1; u<=n; u++ )
res+=edge[match[u]][u];
return res;
}

What's More?

  • 题目可能不存在完备匹配。
  • 这种情况可以在 KM 的基础上在右部点里面加虚点使得可以形成完备匹配,然后再加虚边。显然,如果被迫选了虚边,那在原图的情况上就是无解,所以把虚边的权值赋成 \(-inf\) 即可(这是针对无解的情况,如果是非完备匹配那就设成 0 )。

参考

George1123的题解 话说 George 的找遍全网真的不是看的百度百科或者wiki吗

稳定婚姻问题

提出问题

给一张完全二分图,每一对左边的点与右边的点之间都有一个评分 \(w_{i,j}\) ,要求把点两两匹配,满足:

设点 \(a,c\) 在同一边,点 \(b,d\) 在另一边,当前 \((a,b),(c,d)\) 匹配。那么对于所有这种 \((a,b,c,d)\) 不能存在 \(w_{b,a}<w_{b,c}\) ( \(b\) 认为 \(c\) 比 \(a\) 优)且 \(w_{c,b}>w_{c,d}\) ( \(c\) 认为 \(b\) 比 \(d\) 优)的情况。

如果存在,那么显然 \(b,c\) 会组成一对,因为这对于 \(b,c\) 来说都更优,那么他们会各自拒绝自己原来的匹配,就不稳定了。

分析问题

流程

定义 \(match[i]\) 表示点 \(i\) 所匹配的点。

每次取出一个没有匹配的左部点 \(u\) ,

  • 如果 \(u\) 的最优匹配 \(v\) 没有匹配,那么 \(u,v\) 匹配
  • 如果 \(v\) 有匹配,且对于点 \(v\) ,\(u\) 比它当前的匹配点更优,那么 \(u,v\) 匹配,\(v\) 原先的匹配点失配

直到每个点都有匹配位置。

正确性证明

  • 可行性:假设结束后,有一个左部点和一个右部点没有匹配。

    • 如果一个左部点一直没有匹配,显然会尝试和所有右部点匹配;
    • 如果一个右部点被左部点尝试过了,那么一定会有匹配;

    所以右部点一定有匹配,不会出现失配的情况。

  • 复杂度:每个左部点最多尝试 \(n\) 次与右部点匹配,上界为 \(\mathcal{O}(n^2)\) .

性质

  • 主动方可以选择到他可以匹配到的最优的匹配。所以是对男生有优势是吗(

解决问题

模板题链接 (洛谷那个长得像模板题的东西是假的,那个是 Tarjan 求 SCC)

//Author: RingweEH
//稳定婚姻问题模板,POJ3487
const int N=40;
int lef[N],list_lr[N][N],list_rl[N][N],nxt_l[N];
int match_l[N],match_r[N],n;
queue<int> q; void get_match( int le,int ri )
{
int now=match_r[ri];
if ( now )
{
match_l[now]=0; q.push( now );
}
match_r[ri]=le; match_l[le]=ri;
} int main()
{
ios::sync_with_stdio(0);
int T=read();
while ( T-- )
{
memset( lef,0,sizeof(lef) );
cin>>n; char ch;
for ( int i=0; i<n; i++ )
{
cin>>ch; lef[ch-'a'+1]=1;
}
for ( int i=0; i<n; i++ )
cin>>ch;
char s[N];
for ( int i=0; i<n; i++ )
{
cin>>s; int c=s[0]-'a'+1;
for ( int j=2; s[j]; j++ )
list_lr[c][j-1]=s[j]-'A'+1;
nxt_l[c]=1; match_l[c]=0;
q.push( c );
}
for ( int i=0; i<n; i++ )
{
cin>>s; int c=s[0]-'A'+1;
for ( int j=2; s[j]; j++ )
list_rl[c][s[j]-'a'+1]=j-1;
match_r[c]=0;
}
//-------------Input Finished.--------------
while ( !q.empty() )
{
int now=q.front(); q.pop();
int rnow=list_lr[now][nxt_l[now]++];
if ( !match_r[rnow] ) get_match( now,rnow );
else if ( list_rl[rnow][now]<list_rl[rnow][match_r[rnow]] ) get_match( now,rnow );
else q.push( now );
}
//-------------Matched---------------
for ( int i=1; i<35; i++ )
if ( lef[i] ) cout<<(char)(i-1+'a')<<' '<<(char)(match_l[i]+'A'-1)<<endl;
if ( T ) cout<<endl;
} return 0;
}

图论补档——KM算法+稳定婚姻问题的更多相关文章

  1. 稳定婚姻问题和Gale-Shapley算法(转)

    什么是算法?每当有人问作者这样的问题时,他总会引用这个例子:假如你是一个媒人,有若干个单身男子登门求助,还有同样多的单身女子也前来征婚.如果你已经知道这些女孩儿在每个男孩儿心目中的排名,以及男孩儿们在 ...

  2. Ladies' Choice UVALive - 3989 稳定婚姻问题 gale_shapley算法

    /** 题目: Ladies' Choice UVALive - 3989 链接:https://vjudge.net/problem/UVALive-3989 题意:稳定婚姻问题 思路: gale_ ...

  3. 算法笔记_138:稳定婚姻问题(Java)

    目录 1 问题描述 2 解决方案   1 问题描述 何为稳定婚姻问题? 有一个男士的集合Y = {m1,m2,m3...,mn}和一个女士的计划X = {n1,n2,n3,...,nn}.每一个男士有 ...

  4. 【bzoj2140】: 稳定婚姻 图论-tarjan

    [bzoj2140]: 稳定婚姻 哎..都是模板题.. 一眼看过去 哇 二分图哎 然后发现好像并不能匈牙利算法 自己xjb画两张图,发现二分图左向右连配偶的边,然后右向左连交往过的边 然后如果Bi G ...

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

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

  6. 图论:KM算法

    如果,将求二分图的最大匹配的所有匹配边的权重看做1 那么用匈牙利算法求二分图的最大匹配的问题也可以看成求二分图的最大权匹配 如果边权是特例,我们就要使用KM算法来做了 这个算法其实还是比较难的,会用就 ...

  7. 【稳定婚姻问题】【HDU1435】【Stable Match】

    2015/7/1 19:48 题意:给一个带权二分图  求稳定匹配 稳定的意义是对于某2个匹配,比如,( a ---- 1) ,(b----2) , 如果 (a,2)<(a,1) 且(2,a)& ...

  8. 【算法笔记】二分图与KM算法(当你试图只看蓝书学算法

    前言 呜,好久没写博客了,DDL 也有好多,一不留神就轮到我了呜. 看了一眼其它同学写的博客,什么数模啊,什么 CTF 啊,什么 Python 爬虫啊,感觉自己真是越来越菜了呜. 然后在我一愁莫展之际 ...

  9. km算法的个人理解

    首先相对于上个blog讲的匈牙利算法用于解决无权二分图的最佳匹配,km算法则是在匈牙利算法基础上更进一层的,每条边增加了权值后,真的开始看时有些无厘头,觉得没有什么好方法,但两位牛人Kuhn-Munk ...

随机推荐

  1. binary hacks读数笔记(od命令)

    Linux od命令用于输出文件内容. od指令会读取所给予的文件的内容,并将其内容以八进制字码呈现出来 -t<输出格式>或--format=<输出格式> 设置输出格式. 实例 ...

  2. Proftp最简匿名访问配置

    前言 每一次做ftp的配置都要弄半天,找文档,各种权限控制的坑,折腾半天,这次还是准备记录下来,以备不时之需,这里不配置什么高级的功能,就去实现一个最简单的配置 匿名用户的上传和下载 配置proftp ...

  3. 读取由FileProvider创建的Uri路径文件

    val uri = intent.clipData.getItemAt(0).uri //读取由FileProvider传递的uri文件val fileDecript= contentResolver ...

  4. 美团 Java 面试 154 道题分享!

    Java集合22题 ArrayList 和 Vector 的区别. 说说 ArrayList,Vector, LinkedList 的存储性能和特性. 快速失败 (fail-fast) 和安全失败 ( ...

  5. php7的Opcache getshell

    OPcache基础 OPcache是一种通过解析的PHP脚本预编译的字节码存放在共享内存中来避免每次加载和解析PHP脚本的开销,解析器可以直接从共享内存读取已经缓存的字节码,从而大大提高了PHP的执行 ...

  6. PHP的命令执行漏洞学习

    首先我们来了解基础 基础知识来源于:<web安全攻防>徐焱 命令执行漏洞 应用程序有时需要调用一些执行系统命令的函数,如在PHP中,使用system.exec.shell_exec.pas ...

  7. webug第七关:越权

    第七关:越权 观察url 将name换成admin 更改了admin的密码

  8. webug第四关:告诉你了flang是5位数

    第四关:告诉你了flang是5位数 开始看到有点懵 于是不要脸的看源码 burp跑弱口令

  9. 打包错误:Failed to execute goal org.scala-tools:maven-scala-plugin:2.15.2:compile (default) on project MusicProject: wrap: org.apache.commons.exec.ExecuteException:

    错误:Failed to execute goal org.scala-tools:maven-scala-plugin:2.15.2:compile (default) on project Mus ...

  10. 企业级工作流解决方案(十)--集成Abp和ng-alain--权限系统

    权限系统 应用系统离不开权限控制,权限中心不一定能抽象出所有的业务场景,这里定义的权限系统不一定能够满足所有的场景,但应该可以满足多数的业务需求. Abp的zero项目也定义了权限相关的表,但里面很多 ...