二分图最大匹配 -- 匈牙利算法

2024-06-14 15:48

本文主要是介绍二分图最大匹配 -- 匈牙利算法,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!


Algorithm.( Augmenting Path Algorithm )


Input:
    An X-Y bigraph G, a matching M in G,
    and the set U of M-unsaturated vertices in X.
        
Idea:
    Explore M-alternating paths form U,
    letting S ⊆ X and T ⊆ Y be the sets of vertices reached.
    Marks vertices of S that have been explored for path extensions.
    As a vertex is reached, record the vertex from which it is reached.
        
Initialization:
    S = U and T = ∅


Iteration:
    If S has no unmarked vertex,
    stop and report T ∪ ( X - S ) as a minimun cover and M as a maximum matching.
    Otherwise, select an unmarked x ∈ S, consider each y ∈ N( x ) such that xy ∉ M,
    if y is unsaturated, terminate and report an M-augmenting path from U to y.
    Otherwise, y is matched to some w ∈ X by M.
    In this case, include y in T ( reached from x ) and include w in S ( reached from y ).
    After exploring all such edges incident to x, mark x and iterate.


#include <iostream>
#include <cstring>
using namespace std;#define NODE_SIZE 100bool bigraph[NODE_SIZE][NODE_SIZE];
int parent[NODE_SIZE];
bool visit[NODE_SIZE];int num = 0;
int nodes1, nodes2, edges;bool find_augmenting_path( int start_node ){for( int end_node = 1; end_node <= nodes2; ++end_node ){if( bigraph[start_node][end_node] && visit[end_node] == false ){visit[end_node] = true;if( parent[end_node] == 0 ||find_augmenting_path( parent[end_node] ) ){parent[end_node] = start_node;return true;}}}return false;
}int main(){memset( bigraph, false, sizeof( bigraph ) );memset( visit, false, sizeof( visit ) );memset( parent, 0, sizeof( parent ) );cin >> nodes1 >> nodes2 >> edges;for( int i = 1; i <= edges; ++i ){int start_node, end_node;cin >> start_node >> end_node;bigraph[start_node][end_node] = true;}for( int node = 1; node <= nodes1; ++node ){memset( visit, false, sizeof( visit ) );if( find_augmenting_path( node ) )num++;}cout << num << endl;return 0;
}


简单应用:

在 raw * col 的棋盘上,有些格子不能放,用1 * 2 的方块铺棋盘,问能不能铺满?

比如:

思路:

对每种格子着色,黑白相间,不能放的格子除。

然后白色的格子为一个部集,黑色的格子也为一个部集,两个部集进行最大匹配,

若最后匹配数目等于黑色或者白色格子的总数,即为可行;否则,不可行。


法1:

// 1143MS
#include <iostream>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;#define CHESSBOARD_SIZE 50
#define GRAPH_SIZE 1100bool chessboard[CHESSBOARD_SIZE][CHESSBOARD_SIZE];
bool bigraph[GRAPH_SIZE][GRAPH_SIZE];
bool visit[GRAPH_SIZE];
int parent[GRAPH_SIZE];
int raws, cols, holes;bool find_augmenting_path( int source ){for( int target = 1; target <= raws * cols; ++target ){if( bigraph[source][target] && !visit[target] ){visit[target] = true;if( parent[target] == 0 || find_augmenting_path( parent[target] ) ){parent[target] = source;return true;}}}return false;
}int maximum_matching(){int ans = 0;for( int i = 1; i <= raws * cols; ++i ){memset( visit, false, sizeof( visit ) );if( find_augmenting_path( i ) )ans++;}return ans;
}int main(){memset( chessboard, false, sizeof( chessboard ) );memset( bigraph, false, sizeof( bigraph ) );memset( visit, true, sizeof( visit ) );memset( parent, 0, sizeof( parent ) );cin >> raws >> cols >> holes;int num = raws * cols;for( int i = 1; i <= holes; ++i ){int raw, col;cin >> col >> raw;chessboard[raw][col] = true;num--;}for( int raw = 1; raw <= raws; ++raw ){for( int col = 1; col <= cols; ++col ){if( chessboard[raw][col] )continue;int p1 = cols * ( raw - 1 ) + col;if( raw > 1 && chessboard[raw - 1][col] == false ){int p2 = p1 - cols;bigraph[p1][p2] = true;}if( raw < raws && chessboard[raw + 1][col]== false ){int p2 = p1 + cols;bigraph[p1][p2] = true;}if( col > 1 && chessboard[raw][col - 1] == false ){int p2 = p1 - 1;bigraph[p1][p2] = true;}if( col < cols && chessboard[raw][col + 1] == false ){int p2 = p1 + 1;bigraph[p1][p2] = true;}}}int ans = maximum_matching();if( ans == num )cout << "YES" << endl;elsecout << "NO" << endl;return 0;
}

法2:

// 725k 32MS
#include <iostream>
#include <vector>
#include <cstring>
using namespace std;#define NODE_SIZE 50struct Position{Position(){raw = -1;col = -1;};Position( int r, int c ){raw = r;col = c;};int raw;int col;
};enum State { INIT, HOLE, BLACK, WHITE };
State chessboard[NODE_SIZE][NODE_SIZE];Position parent[NODE_SIZE * NODE_SIZE];
bool visit[NODE_SIZE * NODE_SIZE];
int raws, cols, holes;int direction[5][3] = {{ 0, 0, 0 },{ 0, 1, 0 },{ 0, 0, 1 },{ 0, -1, 0 },{ 0, 0, -1 }
};bool find_augmenting_path( Position source ){for( int i = 1; i <= 4; ++i ){int dx = direction[i][1];int dy = direction[i][2];int next_raw = source.raw + dx;int next_col = source.col + dy;if( next_raw >= 1 &&next_raw <= raws &&next_col >= 1 &&next_col <= cols ){int one_dim_pos = cols * ( next_raw - 1 ) + next_col;if( chessboard[next_raw][next_col] == WHITE && !visit[one_dim_pos] ){visit[one_dim_pos] = true;if( ( parent[one_dim_pos].raw == -1 &&parent[one_dim_pos].col == -1 ) ||find_augmenting_path( parent[one_dim_pos] ) ){parent[one_dim_pos].raw = source.raw;parent[one_dim_pos].col = source.col;return true;}}}}return false;
}int main(){cin >> raws >> cols >> holes;vector< Position > black_rects;vector< Position > white_rects;for( int raw = 1; raw <= raws; ++raw ){for( int col = 1; col <= cols; ++col ){chessboard[raw][col] = INIT;}}memset( visit, false, sizeof( visit ) );int rects = raws * cols - holes;for( int i = 1; i <= holes; ++i ){int col, raw;cin >> col >> raw;chessboard[raw][col] = HOLE;}for( int raw = 1; raw <= raws; ++raw ){for( int col = 1; col <= cols; ++col ){if( chessboard[raw][col] == HOLE )continue;if( ( col % 2 && raw % 2 ) ||( ( raw % 2 == 0 ) && ( col % 2 == 0 ) ) ){chessboard[raw][col] = BLACK;black_rects.push_back( Position( raw, col ) );}else{chessboard[raw][col] = WHITE;white_rects.push_back( Position( raw, col ) );}}}const int black_rects_num = black_rects.size();const int white_rects_num = white_rects.size();if( black_rects_num == 0 ||rects % 2 != 0 ||black_rects_num != white_rects_num ){cout << "NO" << endl;}else{int ans = 0;for( int i = 0; i < black_rects_num; ++i ){memset( visit, false, sizeof( visit ) );if( find_augmenting_path( black_rects[i] ) )ans++;}if( ans == black_rects_num ){cout << "YES" << endl;}else{cout << "NO" << endl;}}return 0;
}


POJ 3692

建反图,求最大独立集

#include <iostream>
#include <cstring>
using namespace std;#define MAX_SIZE 210bool bigraph[MAX_SIZE][MAX_SIZE];
bool visits[MAX_SIZE];
int parents[MAX_SIZE];
int B, G, M;bool find_augmenting_path( int source ){for( int target = 1; target <= B; ++target ){if( bigraph[source][target] && !visits[target] ){visits[target] = true;if( parents[target] == 0 || find_augmenting_path( parents[target] ) ){parents[target] = source;return true;}}}return false;
}int maximum_matching(){int ans = 0;for( int source = 1; source <= G; ++source ){memset( visits, false, sizeof( visits ) );if( find_augmenting_path( source ) )ans++;}return ans;
}int main(){int nCase = 1;while( true ){memset( bigraph, true, sizeof( bigraph ) );memset( parents, 0, sizeof( parents ) );memset( visits, false, sizeof( visits ) );cin >> G >> B >> M;if( G == 0 && B == 0 && M == 0 )break;for( int i = 1; i <= M; ++i ){int u, v;cin >> u >> v;bigraph[u][v] = false;}int max_matching = maximum_matching();int ans = G + B - max_matching;cout << "Case " << nCase << ": " << ans << endl;nCase++;}return 0;
}


这篇关于二分图最大匹配 -- 匈牙利算法的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1060840

相关文章

SpringBoot实现MD5加盐算法的示例代码

《SpringBoot实现MD5加盐算法的示例代码》加盐算法是一种用于增强密码安全性的技术,本文主要介绍了SpringBoot实现MD5加盐算法的示例代码,文中通过示例代码介绍的非常详细,对大家的学习... 目录一、什么是加盐算法二、如何实现加盐算法2.1 加盐算法代码实现2.2 注册页面中进行密码加盐2.

Java时间轮调度算法的代码实现

《Java时间轮调度算法的代码实现》时间轮是一种高效的定时调度算法,主要用于管理延时任务或周期性任务,它通过一个环形数组(时间轮)和指针来实现,将大量定时任务分摊到固定的时间槽中,极大地降低了时间复杂... 目录1、简述2、时间轮的原理3. 时间轮的实现步骤3.1 定义时间槽3.2 定义时间轮3.3 使用时

如何通过Golang的container/list实现LRU缓存算法

《如何通过Golang的container/list实现LRU缓存算法》文章介绍了Go语言中container/list包实现的双向链表,并探讨了如何使用链表实现LRU缓存,LRU缓存通过维护一个双向... 目录力扣:146. LRU 缓存主要结构 List 和 Element常用方法1. 初始化链表2.

Nginx中location实现多条件匹配的方法详解

《Nginx中location实现多条件匹配的方法详解》在Nginx中,location指令用于匹配请求的URI,虽然location本身是基于单一匹配规则的,但可以通过多种方式实现多个条件的匹配逻辑... 目录1. 概述2. 实现多条件匹配的方式2.1 使用多个 location 块2.2 使用正则表达式

golang字符串匹配算法解读

《golang字符串匹配算法解读》文章介绍了字符串匹配算法的原理,特别是Knuth-Morris-Pratt(KMP)算法,该算法通过构建模式串的前缀表来减少匹配时的不必要的字符比较,从而提高效率,在... 目录简介KMP实现代码总结简介字符串匹配算法主要用于在一个较长的文本串中查找一个较短的字符串(称为

通俗易懂的Java常见限流算法具体实现

《通俗易懂的Java常见限流算法具体实现》:本文主要介绍Java常见限流算法具体实现的相关资料,包括漏桶算法、令牌桶算法、Nginx限流和Redis+Lua限流的实现原理和具体步骤,并比较了它们的... 目录一、漏桶算法1.漏桶算法的思想和原理2.具体实现二、令牌桶算法1.令牌桶算法流程:2.具体实现2.1

C++使用栈实现括号匹配的代码详解

《C++使用栈实现括号匹配的代码详解》在编程中,括号匹配是一个常见问题,尤其是在处理数学表达式、编译器解析等任务时,栈是一种非常适合处理此类问题的数据结构,能够精确地管理括号的匹配问题,本文将通过C+... 目录引言问题描述代码讲解代码解析栈的状态表示测试总结引言在编程中,括号匹配是一个常见问题,尤其是在

关于Gateway路由匹配规则解读

《关于Gateway路由匹配规则解读》本文详细介绍了SpringCloudGateway的路由匹配规则,包括基本概念、常用属性、实际应用以及注意事项,路由匹配规则决定了请求如何被转发到目标服务,是Ga... 目录Gateway路由匹配规则一、基本概念二、常用属性三、实际应用四、注意事项总结Gateway路由

Python中的随机森林算法与实战

《Python中的随机森林算法与实战》本文详细介绍了随机森林算法,包括其原理、实现步骤、分类和回归案例,并讨论了其优点和缺点,通过面向对象编程实现了一个简单的随机森林模型,并应用于鸢尾花分类和波士顿房... 目录1、随机森林算法概述2、随机森林的原理3、实现步骤4、分类案例:使用随机森林预测鸢尾花品种4.1

如何提高Redis服务器的最大打开文件数限制

《如何提高Redis服务器的最大打开文件数限制》文章讨论了如何提高Redis服务器的最大打开文件数限制,以支持高并发服务,本文给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧... 目录如何提高Redis服务器的最大打开文件数限制问题诊断解决步骤1. 修改系统级别的限制2. 为Redis进程特别设置限制