匈牙利&&EK算法(写给自己看)
(写给自己看)匈牙利算法(最大匹配)和KM算法(最佳匹配)
匈牙利算法
思想
- 不断寻找增广路,每次寻得增广路,交换匹配边和非匹配边,则匹配点数+1
- 这里增广路含义:交错路,即从未匹配点出发经过未匹配边->匹配边->未匹配边->.....->未匹配边
- Konig定理:无权二分图的最大匹配=最小覆盖点集,证明
- 有价值的博客:blog1,blog2,blog3
- 算法其实并不难
模板
模板题目:poj1274,poj1325,后者需要konig定理转化
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include<algorithm>
#include<queue>
#include<stack>
#include<cmath>
using namespace std;
#define ll long long
#define name2str(name) (#name)
#define db(x) cout<<#x"=["<<(x)<<"]"<<endl
#define CL(a,b) memset(a,b,sizeof(a))
#define sf(a) scanf("%d",&a)
#define pr(a) printf("%d\n",a)
#define rng(a) a.begin(),a.end()
#define pb push_back
#define fast ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define fr0(i,m) for(int i=0;i<m;i++)
#define fr1(i,m) for(int i=1;i<=m;i++)
//author:fridayfang
//date:19 3月 12
const double esp=1e-8;
const int mod=1e9+7;
const double pi=acos(-1);
const int inf=0x3f3f3f3f;
const int maxn = 200 + 5;
const int maxm = 1e6+5;
int n,m;
int mpp[maxn][maxn];//mp[i][j] means cow i like stall j
int used[maxn];//used[i] for cow i
int linker[maxn];//linker[i] for stall if -1 not match
//used[i],linker[i],i表示的是右边的点
int dfs(int u){// 实际运行dfs()都是左边的点
for(int i=1;i<=m;i++){
if(mpp[u][i]&&!used[i]){
used[i]=1;
if(linker[i]==-1||dfs(linker[i])){
linker[i]=u;
return 1;
}
}
}
return false;
}
int main(){
while(~scanf("%d %d",&n,&m)){
CL(mpp,0);
fr1(i,n){
int num,v;sf(num);
fr1(j,num){sf(v);mpp[i][v]=1;}
}
//build ok
int ans=0;
CL(linker,-1);
for(int i=1;i<=n;i++){
CL(used,0);
if(dfs(i)) ans++;//每次dfs最多指增加一个点i被匹配,原先已经匹配的点依旧被匹配只是边有变化
}
pr(ans);
}
return 0;
}
KM算法求带权图的最佳匹配
思想
- 设计了顶标的思路,每次在dfs(u)失败后,通过改变增广路上的点的顶标lx[],ly[],来加入次大的边
- 一个边被加入<=>\(lx[u]+ly[v]==mp[u][v]\)
- 顶标变化d,根据满足visx[i]&&!visy[j]的点对来确定,\(d=min(d,lx[i]+ly[j]-mp[i][j])\)
- 有O(N3)和O(n4)写法,后者其实也不是很慢;前者实现一般需要slack[]数组,需要在dfs(u)和顶标变化的时候维护,具体可见代码
- 有价值的blog,blog1,blog2,blog3
模板
- hdu2255模板题
- code1 (O(n^4))(我常用的模板,也比较好写)
#include <bits/stdc++.h>
using namespace std;
#pragma GCC optimize("O3")
#define ll long long
#define ull unsigned long long
#define name2str(name) (#name)
#define db(x) cout<<#x"=["<<(x)<<"]"<<endl
#define CL(a,b) memset(a,b,sizeof(a))
#define sf(a) scanf("%d",&a)
#define pr(a) printf("%d\n",a)
#define rng(a) a.begin(),a.end()
#define pb push_back
#define fast ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define fr0(i,m) for(int i=0;i<m;i++)
#define fr1(i,m) for(int i=1;i<=m;i++)
//author:fridayfang
//date:19 3月 12
const double esp=1e-8;
const int mod=1e9+7;
const double pi=acos(-1);
const int inf=0x3f3f3f3f;
const int maxn = 300 + 5;
const int maxm = 1e6+5;
//先试试O(n^4)写法
int lx[maxn],ly[maxn];
int mat[maxn][maxn];
int n;
int visx[maxn],visy[maxn];
int linker[maxn];
bool dfs(int u){
visx[u]=1;
for(int i=1;i<=n;i++){
if(!visy[i]&&(lx[u]+ly[i]==mat[u][i])){
visy[i]=1;
if(linker[i]==-1||dfs(linker[i])){
linker[i]=u;return true;
}
}
}
return false;
}
int km(){
CL(linker,-1),CL(lx,0),CL(ly,0);
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
lx[i]=max(lx[i],mat[i][j]);
}
}
for(int i=1;i<=n;i++){
while(true){
CL(visx,0),CL(visy,0);
if(dfs(i))break;
int d=inf;
for(int x=1;x<=n;x++){
for(int y=1;y<=n;y++){
if(visx[x]&&!visy[y])d=min(d,lx[x]+ly[y]-mat[x][y]);
}
}
for(int i=1;i<=n;i++){
if(visx[i])lx[i]-=d;
if(visy[i])ly[i]+=d;
}
}
}
int res=0;
for(int i=1;i<=n;i++){
res+=mat[linker[i]][i];
}
return res;
}
int main(){
while(~sf(n)){
fr1(i,n)fr1(j,n)sf(mat[i][j]);
int ans=km();
pr(ans);
}
return 0;
}
- code2 (O(n^3))
/*
实际上,O(n^4)的KM算法表现不俗,使用O(n^3)并不会很大的提高KM的运行效率
需要在O(1)的时间找到任意一条边,使用邻接矩阵存储更为方便
*/
#include <cstring>
#include <cstdio>
const int maxn = 305;
const int INF = 0x3f3f3f3f;
int match[maxn],lx[maxn],ly[maxn],slack[maxn];
int G[maxn][maxn];
bool visx[maxn],visy[maxn];
int n,nx,ny,ans;
bool findpath(int x)
{
int tempDelta;
visx[x] = true;
for(int y = 0 ; y < ny ; ++y){
if(visy[y]) continue;
tempDelta = lx[x] + ly[y] - G[x][y];
if(tempDelta == 0){//(x,y)在相等子图中
visy[y] = true;
if(match[y] == -1 || findpath(match[y])){
match[y] = x;
return true;
}
}
else if(slack[y] > tempDelta)
slack[y] = tempDelta;//(x,y)不在相等子图中且y不在交错树中
}
return false;
}
void KM()
{
for(int x = 0 ; x < nx ; ++x){
for(int j = 0 ; j < ny ; ++j) slack[j] = INF;//这里不要忘了,每次换新的x结点都要初始化slack
while(true){
memset(visx,false,sizeof(visx));
memset(visy,false,sizeof(visy));//这两个初始化必须放在这里,因此每次findpath()都要更新
if(findpath(x)) break;
else{
int delta = INF;
for(int j = 0 ; j < ny ; ++j)//因为dfs(x)失败了所以x一定在交错树中,y不在交错树中,第二类边
if(!visy[j] && delta > slack[j])
delta = slack[j];
for(int i = 0 ; i < nx ; ++i)
if(visx[i]) lx[i] -= delta;
for(int j = 0 ; j < ny ; ++j){
if(visy[j])
ly[j] += delta;
else
slack[j] -= delta;
//修改顶标后,要把所有的slack值都减去delta
//这是因为lx[i] 减小了delta
//slack[j] = min(lx[i] + ly[j] -w[i][j]) --j不属于交错树--也需要减少delta,第二类边
}
}
}
}
}
void solve()
{
memset(match,-1,sizeof(match));
memset(ly,0,sizeof(ly));
for(int i = 0 ; i < nx ; ++i){
lx[i] = -INF;
for(int j = 0 ; j < ny ; ++j)
if(lx[i] < G[i][j])
lx[i] = G[i][j];
}
KM();
}
int main()
{
while(scanf("%d",&n) != EOF){
nx = ny = n;
for(int i = 0 ; i < nx ; ++i)
for(int j = 0 ; j < ny ; ++j)
scanf("%d",&G[i][j]);
solve();
int ans = 0;
for(int i = 0 ; i < ny ; ++i)
if(match[i] != -1)
ans += G[match[i]][i];
printf("%d\n",ans);
}
return 0;
}
匈牙利&&EK算法(写给自己看)的更多相关文章
- 最大网络流 EK 算法
网络流是什么类型的问题,看一道题目你就知道了 点击打开链接 . 默认具备图论的基本知识,网络流概念比较多,先看看书熟悉一下那些概念.比较好!一个寄出的网络最大流.EK算法写的. 这是一幅网络,求S ...
- POJ 1459 网络流 EK算法
题意: 2 1 1 2 (0,1)20 (1,0)10 (0)15 (1)20 2 1 1 2 表示 共有2个节点,生产能量的点1个,消耗能量的点1个, 传递能量的通道2条:(0,1)20 (1,0) ...
- 【算法•日更•第三十五期】FF算法优化:EK算法
▎写在前面 FF算法传送门 之前我们已经学过了FF算法(全称Ford-Fulkerson算法)来找最大流,但是这种算法仍有诸多不对的地方. 其实这种算法存在着严重的效率的问题,请看下面的图: 以这个图 ...
- vector实现最大流EK算法
序: 在之前的文章中实现了不利用STL实现EK算法,效率也较高.这次我们企图简化代码,减少变量的使用与手写模拟的代码. 注意:vector等STL的container在不开O2优化的时候实现同一个效果 ...
- 二分图的最大匹配——最大流EK算法
序: 既然是个图,并且求边数的最大值.那么这就可以转化为网络流的求最大流问题. 只需要将源点与其中一子集的所有节点相连,汇点与另一子集的所有节点相连,将所有弧的流量限制置为1,那么最大流 == 最大匹 ...
- 【读书笔记】《写给大忙人看的Java SE 8》——Java8新特性总结
虽然看过一些Java 8新特性的资料,但是平时很少用到,时间长了就忘了,正好借着Java 9的发布,来总结下一些Java 8中的新特性. 接口中的默认方法和静态方法 先考虑一个问题,如何向Java中的 ...
- 网络最大流算法—EK算法
前言 EK算法是求网络最大流的最基础的算法,也是比较好理解的一种算法,利用它可以解决绝大多数最大流问题. 但是受到时间复杂度的限制,这种算法常常有TLE的风险 思想 还记得我们在介绍最大流的时候提到的 ...
- POJ3436 ACM Computer Factory【EK算法】
题意: 每个电脑需要P个组成部分,现有N的机器,每个机器都可以对电脑进行加工,不过加工的前提是某些部分已经存在,加工后会增加某些部分.且在单位时间内,每个机器的加工都有一个最大加工容量,求能得到的最大 ...
- 【最大流之ek算法】HDU1532 求最大流
本来是继续加强最短路的训练,但是遇到了一个最短路 + 最大流的问题,最大流什么鬼,昨天+今天学习了一下,应该对ek算法有所了解,凭借学习后的印象,自己完成并ac了这个最大流的模板题 题目大意:都是图论 ...
随机推荐
- 0709关于mysql优化思路【何登成】
转自 http://isky000.com/database/mysql-performance-tuning-sql 优化目标 减少 IO 次数IO永远是数据库最容易瓶颈的地方,这是由数据库的职责所 ...
- nginx中父子进程工作的主体函数
依据Nginx(0.7.67版本号)的代码.对Nginx主要的进程创建,进程主体以及事件处理进行了简要的分析. 基本上,父进程(即主进程)一開始会初始化及读取配置.并载入各模块的功能,然后fork() ...
- sqlserve 数据类型具体解释
decimal 精确数值型 decimal 数据类型能用来存储从-10的38次幂-1到10的38次幂-1的固定精度和范围的数值型数据.使用这样的数据类型时,必须指定范围和精度. 范围是小数点左右 ...
- [WPF]c#调用默认浏览器打开网址
//调用系统默认的浏览器 System.Diagnostics.Process.Start("http://www.zhaokeli.com");
- Codeforces Round #200 (Div. 2)D. Alternating Current (堆栈)
D. Alternating Current time limit per test 1 second memory limit per test 256 megabytes input standa ...
- instanceof运算符的使用
在之前的学习中,经常遇到instanceof运算符,对于它的用法总感觉理解不到位,所以专门总结一下它的用法加深理解. instanceof主要用来判断一个类是否实现了某个接口,或者判断一个实例对象是否 ...
- 国内物联网平台初探(一) ——百度物接入IoT Hub
物接入IoT Hub - 架构 全托管的云服务,帮助建立设备与云端之间安全可靠的双向连接 支撑海量设备的数据收集.监控.故障预测等各种物联网场景 物接入IoT Hub - 功能 通信协议:支持MQTT ...
- c++ stl swap
好久没写了,简单水一下吧! 一个是最近没怎么刷题,圣诞,元旦,leetcode暂停的比赛两周,自己最近沉迷于打游戏,没有好好抓住时间. 其实最近看了一点书,是侯捷的 <stl源码剖析>,有 ...
- C - Pocket Book(set)
Problem description One day little Vasya found mom's pocket book. The book had n names of her friend ...
- DevExpress的GridControl拖拽DraopDown后计算HitInfo的RowHandle错误
最近在使用GridControl的拖拽功能时候遇到了一个问题:当GridControl触发DropDrop事件时,计算对应的RowHandle错误.当把鼠标拖拽到GridView一个单元格的靠上面的部 ...