Coloring Algorithm and Hungarian Algorithm for Analyzing Bipartite Graphs in C++ Graph Advanced Series

1 Introduction

二分图Also known as 二部图or called 偶图, it is a kind of graph theory 特殊类型and has a wide range of application scenarios.

What is a bipartite graph?

  • Bipartite graphs generally refer to undirected graphs. To look at the problem, you must have a philosophical thought, and a bipartite graph can also be a directed graph.

  • If all the graphs 顶点集合can be divided into two independent subsets, and there is no edge connection between any vertices in any subset, then such a graph is said to be bipartite.

The graph structure in the following figure can be called 二分图.

1.png

Features of bipartite graphs:

  • In theory, there is at least one in the graph . If there is no cycle in the graph, the graph degenerates into a tree. When studying trees and graphs, it is common to think of tree problems as a subclass of graph problems.
  • 二分图There cannot be a cycle with an odd number of vertices in .

How to verify that a cycle in a bipartite graph cannot have an odd number of vertices?

  • A ring, also known as a loop, refers to a path whose starting point and ending point are the same vertex.
  • To prove this problem, you can use the coloring algorithm , which is a classic algorithm for judging bipartite graphs.

2. Coloring algorithm

二分图The definition of has stated that there are two independent subsets in the graph. In order to distinguish these two subsets, the vertices in one of the subsets can be dyed red, and the vertices in the other subset can be dyed blue. The specific color is not important, as long as it can be distinguished.

The essence of the coloring algorithm:

  • Use DFSor BFSto traverse the graph and color all vertices in the graph.
  • Once a vertex is found to have the same color as its adjacent vertices, it can be determined that the graph structure is not a bipartite graph.

It should be predictable in terms of perception. If it is an odd-numbered ring, there must be at least one pair of adjacent vertices with the same color.

2.1 Dyed even rings

Is the following 图结构a bipartite graph?

There图结构 is a cycle, and the number of vertices forming the ring is even.
2.png

The process of using the coloring algorithm to determine is as follows:

  • Starting from the vertex numbered 1, color it red and mark it as a member of the red subset.

3.png

  • Find the numbered 1adjacent vertices 2和6. Because vertices in the same subset cannot be connected by edges. The vertices numbered 2and 6can not be 1in the same subset as the vertices numbered, so the vertices numbered 2and 6can only exist in another subset, so it is marked in blue.

4.png

  • Find 2the vertex adjacent to the number 3. According to the definition of the bipartite graph, the vertex with the number 3can only be dyed red. In the same way, 6vertices adjacent to the number 5can only be dyed red.

5.png

  • Vertices numbered are adjacent vertices 4numbered 3and . 5Obviously, only blue can be dyed.

6.png

  • After traversing all nodes, each node is colored, and the vertices at both ends of all edges have different colors (this is the basis for judging success) . It can be judged that this graph is a bipartite graph. Slightly deform the graph structure.

7.png

2.2 Coloring odd rings

Come to a 奇数graph structure of a ring, and use the coloring algorithm to judge whether the graph is a bipartite graph.

8.png

  • Start with numbering 1, color as 红色.

9.png

  • Find the numbered 1adjacent vertices 2、5. Dyed blue.

10.png

  • Find the numbered 2adjacent vertices 3and color them red. Numbered 5adjacent vertices 4, colored red.

11.png

  • Vertices numbered 3and 4are colored red. According to the definition of a bipartite graph, adjacent vertices cannot have the same color. Therefore, when the number of vertices of the cycle is odd, the graph is not bipartite.

To sum up:

The coloring algorithm is to color the vertices in the graph:

  • If the colors at both ends of all edges are not the same, the graph can be considered as bipartite.
  • If only one edge in the final graph has the same color at both ends, the graph can be considered not bipartite.

2.3 Encoding implementation

In the following coding implementation, use 1means red, -1means blue, 0means no dyeing.

Vertex type:

#include <iostream>
#include <vector>
using namespace std;
/*
* 顶点类型
*/
struct Vertex {
	//编号
	int vid;
	//数据
	int val;
	//颜色 0:没染色,  1: 红色 -1: 蓝色
	int color;
	//邻接节点
	vector<int> edges;
	Vertex() {
        //默认没有染色
		this->color=0;
	}
	Vertex(int vid,int val,int color  ) {
        //默认没有染色
		this->color=0;
		this->vid=vid;
		this->val=val;
	}
	/*
	*添加邻接顶点
	*/
	void addEdge(int vid) {
		this->edges.push_back(vid);
	}
};

Graph class: Only the core logic is reflected in the function, which weakens the code such as verification. The vertices of the graph are numbered from 1the beginning.

class Graph {
	private:
		//所有顶点
		vector<Vertex*> allVertexs;
		//顶点编号
		int number;
	public:
		Graph() {
			this->number=1;
			//0 位置填充一个占位符。简化操作,顶点的编号值即为其存储位置编号
			this->allVertexs.push_back(NULL);
		}
    
		/*
		*添加顶点,返回顶点的编号。没有检查顶点是否存在
		*/
		int addVertex(int val) {
			//创建新的顶点
			Vertex* ver=new Vertex(number++,val,0);
			this->allVertexs.push_back(ver);
			return ver->vid;
		}
    
		/*
		* 添加邻接边
		*/
		void addEdge(int fromId,int toId) {
			//无向图中顶点间双向关系
			this->allVertexs[fromId]->addEdge(toId);
			this->allVertexs[toId]->addEdge(fromId);
		}

		/*
		* 深度搜索(也可以使用广度搜索)算法对图中的顶点染色
		* 1 :表示红色
		* -1:表示蓝色
		*/
		bool fillColor(int fromId,int color) {
			//当前顶点的颜色
			this->allVertexs[fromId]->color=color;
			//找到所有邻接顶点
			vector<int> vers=this->allVertexs[fromId]->edges;
			for(int i=0; i<vers.size(); i++) {
				if(this->allVertexs[vers[i]]->color!=0) {
					//已经染过颜色
					if( this->allVertexs[vers[i]]->color==color  ) {
						//如果和邻接顶点颜色相同
						return false;
					}
				} else {
					//没染色,深度搜索
					return fillColor(vers[i],-1*color);
				}
			}
			return true;
		}
};

Test code:

  • Test whether the following graph is a bipartite graph with cycles consisting of an even number of vertices.

2.png

int main(int argc, char** argv) {
	Graph* graph=new Graph();
	for(int i=1; i<=6; i++)
		graph->addVertex(i);
	graph->addEdge(1,2);
	graph->addEdge(1,6);
	graph->addEdge(2,3);
	graph->addEdge(3,4);
	graph->addEdge(4,5);
	graph->addEdge(5,6);
	string fill=graph->fillColor(1,1)?"是二分图":"不是二分图";
	cout<<fill<<endl;
	return 0;
}

Output conclusion:

12.png

  • Test an odd number of ring graphs.
int main(int argc, char** argv) {
	Graph* graph=new Graph();
	for(int i=1; i<=5; i++)
		graph->addVertex(i);
	graph->addEdge(1,2);
	graph->addEdge(1,5);
	graph->addEdge(2,3);
	graph->addEdge(3,4);
	graph->addEdge(4,5);
	string fill=graph->fillColor(1,1)?"是二分图":"不是二分图";
	cout<<fill<<endl;
	return 0;
}

Output result:

13.png

  • A bipartite graph does not necessarily have to have a ring structure.
    14.png
int main(int argc, char** argv) {
	Graph* graph=new Graph();
	for(int i=1; i<=7; i++)
		graph->addVertex(i);
	graph->addEdge(1,2);
	graph->addEdge(2,3);
	graph->addEdge(3,4);
	graph->addEdge(4,5);
	graph->addEdge(5,6);
	graph->addEdge(6,7);
	string fill=graph->fillColor(1,1)?"是二分图":"不是二分图";
	cout<<fill<<endl;
	return 0;
}

Output result:

15.png

3. Bipartite graph maximum matching

3.1 Hungarian Algorithm Thought

First understand what is the maximum matching concept of a bipartite graph.

A bipartite graph divides the vertices of the graph into two subsets, such as uses nand mrepresentations. It is required to select some edges, and the edges without common vertices among all edges are called matching edges, and the algorithm for finding the most matching edges is the maximum matching algorithm.

As shown in the figure below, the edges marked in red are matching edges, and the blue edges are non-matching edges . And the maximum number of matches is 3.

17.png

Think of nthe collection as a group of men on a blind date, and mthe collection as a group of women. A man can be associated with multiple women, however, a man can only end up marrying one woman. That is, if n和mthe vertices in the set have been selected as matching edges, the edges connecting these two vertices to other vertices must be non-matching edges.

Algorithm for finding the maximum matching edge of a bipartite graph:

  • Use the augmented path to find the maximum matching (called the Hungarian algorithm, proposed by the Hungarian mathematician Edmonds in 1965).
  • Convert to network flow model.

This article only explains the Hungarian algorithm, and those who are interested in the network flow algorithm can understand it by themselves.

Before using the Hungarian algorithm, you need to understand two concepts:

  • Alternate road : Starting from an unmatched point, passing through an unmatched edge, a matched edge, and an unmatched edge in turn... Such a road is called an alternate road.
  • Zengguang Road : Starting from an unmatched point, take an alternate road and arrive at an unmatched point. This road is called Zengguang Road.

As shown in the figure below, the known sum (3.4)is (5,6)a matching edge 3、4、5、6and a matching vertex.

18.png

Then, 2->3->4->5->6->1it is an augmenting path.

19.png

Zengguang Road has the following characteristics:

  • An augmenting path has an odd number of edges.
  • The points on the path must belong to two subsets.
  • The start and end points are points that have not yet been paired.
  • The number of unmatched edges is greater than the number of matched edges 1, and the reason for this should be well understood.

The core idea of ​​the Hungarian algorithm:

  • Enumerate all unmatched points and find augmented paths.
  • Until no augmentation path is found.

The flow of the Hungarian algorithm is described as follows:

  • Find the maximum matching of the following structure. Initially all vertices are non-matching vertices and all edges are non-matching edges. Prepare an array to store matching edges. The array subscript indicates the vertex number, and the value indicates the matching vertex.

20.png

  • With 1the vertex numbered as the starting point, the depth search finds the augmenting path (terminating at the non-matching point). Then (1,2)and (1,6)are both valid choices, choose (1,2). According to the definition of Zengguang Road, this Zengguang Road cannot be extended. Set 1to match points.

21.png

  • The feature of the Hungarian algorithm is to scan all vertices, and use each vertex as the starting point to search in depth to find the augmenting path.

    Then 2start from the vertex numbered as . As shown in the figure below, 2it will become a matching vertex.

    Note that as long as there are augmenting paths in the graph, the matching information recorded is not the final result, and the matching information may be updated.

23.png

  • Take the vertex 3as the starting point. 3->2->1->6Search by path, because it 6is a non-matching point, Zengguang Road ends at 6, and set 1as a matching point.

    It should be noted here that during the recursive upward process, the matching point numbered will be 2modified 3.

24.png

  • Take 4the vertex numbered as the starting point. Go 4->5, because 5it is a non-matching point, it is set 4as 5a matching point.

25.png

  • Take the number 5as the starting point, follow 5->4the route, and set it 5as 4the matching point.

29.png

  • Starting from the vertex numbered 6, set the matching point 6numbered 1. The edge between the matching points is drawn in red color, which is the maximum matching edge.

27.png

3.2 Encoding implementation

The prerequisite for using the Hungarian algorithm: the graph must be a bipartite graph.

#include <iostream>
#include <vector>
#include <cstring>
#define maxn 10
using namespace std;

//存储边信息 
vector<int> edges[maxn];
//存储匹配边    
int match[maxn];
//每一次搜索过程中记录顶点状态     
bool vis[maxn];    
int n,m;           
/*
* 深度搜索查找增广路 
*/ 
bool dfs(int x) {
	for(int i=0; i<edges[x].size(); i++) {
		//子节点 
		int v = edges[x][i];
		if(vis[v] == false) {
		    //避免重复访问      
			vis[v] = true;
			/*
			* 1、 如果是非匹配点,直接匹配
			* 2、 否则,继续深度搜索 
			*/ 
			if(match[v] == -1 || dfs(match[v])) {     
				match[v] = x;
				return true;
			}
		}
	}
	return false;
}
int find() {
	n=6;
	m=6;
	edges[1].push_back(2);
	edges[1].push_back(6);
	edges[2].push_back(3);
	edges[2].push_back(1);
	edges[3].push_back(2);
	edges[3].push_back(4);
	edges[4].push_back(5);
	edges[4].push_back(3);
	edges[5].push_back(4);
	edges[5].push_back(6);
	edges[6].push_back(1);
	edges[6].push_back(5);

	int sum = 0;
	memset(match,-1,sizeof(match));

	for(int i=1; i<=n; i++) {
		memset(vis,false,sizeof(vis));     
		if(dfs(i)) sum ++;
	}
	return sum;
}

int main() {
	int res= find();
	cout<<res<<endl;
	for(int i=1;i<=n;i++){
		cout<<match[i]<<"\t";
	}
	return 0;
}

After the code is executed, matchthe content in the array is shown in the following figure:

28.png

There are two-way records in the array, and the actual maximum matching edge is only 3.

26.png

4. Summary

This article explains the definition of a bipartite graph and how to use the coloring algorithm to determine whether a graph is a bipartite graph. And explain the Hungarian algorithm for finding the maximum matching edge.

Essentially, they are all implemented based on deep search.

Guess you like

Origin blog.csdn.net/y6123236/article/details/130703153