二分图最大匹配 -- 匈牙利算法
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 100 bool 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 1100 bool 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; else cout << "NO" << endl; return 0; }
法2:
// 725k 32MS #include <iostream> #include <vector> #include <cstring> using namespace std; #define NODE_SIZE 50 struct 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 210 bool 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; }