主要包括图论涉及到的常用算法模版。
可以说,图论基础知识(一)中,主要探讨了主要的图论知识点,因为ACM比赛的缘故,很多知识体系可以理解成为了比赛而收集得来的,主要的知识结构还是建议学习一下离散数学这门课,之后再涉及相关的数据结构,可能会事半功倍,本节主要将大部分图论的算法收集整合一下,基本都是比赛常用的,欢迎各位收藏。
主要涉及的有最小生成树,最短路径,二分图,连通分量等,首先我得先感谢我的学长将大部分算法收集完成了,我只是在此基础上进行相关的修改完善。
注意:以上内容仅仅只是算法,并不是问题的完整题解,详细经典题解我会在之后补充,可以收集作为模版使用
最小生成树
最小生成树(详细定义参考离散数学以及数据结构),主要涉及的算法有kruskal算法,prim算法在此基础上修正kruskal+ufs,算法如下:
kruskal+ufs
1 | int ufs(int x) { |
prim
1 | int Prim() { |
最短路径
有谈到最短路径了,这也是之前我开始准备图论知识的起源吧,话不多说主要算法如下:
Dijkstra+priority_queue
1 | void Dijkstra(int s) { |
Bellman-Ford(SPFA)
1 | void BellmanFord(int s) { // SPFA |
FLoyd
1 | void Floyd() { |
二分图
ufs 验证 Hungary
1 | bool DFS(int u) { |
连通分量
Tarjan stack
1 | stack<int> s; |
网络流
网流量的相关问题,主要阐述一下即可,费用流的问题,可以利用Bellman-Ford
找增广路,或者贪心算法
即可,最大流问题即用Edmonds-KarpS
主要介绍一下最大流问题的算法:
通过BFS的方式来进行增广路径的查找,即每次找步数最少的路径进行残留网络的修改,此方法的复杂度为O(VE^2).1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51int graph::Edmonds_Karp(int start, int end, int remain_g[][1000], int current_g[][1000])//按BFS找增广路径(即每次找边数最短的)实现Ford_Fulkerson方法
{
for (int i = 0; i < Pnum; i++)
for (int j = 0; j < Pnum; j++)
{
if (edge[i][j] == INF)
remain_g[i][j] = 0;
else
remain_g[i][j] = edge[i][j];
}
int max_flow = 0;
while (1)
{
int visit[1000];
int pre[1000];//记录节点的前驱,好找路径,其实这里的visit可以不要,就用pre
memset(visit, 0, sizeof(visit));
memset(pre, 0, sizeof(pre));
//bfs在残留网络中找增广路径
queue<int> q;
q.push(start);
while (!q.empty())
{
int temp_point = q.front();
q.pop();
if (temp_point == end)
break;
for (int i = 0; i < Pnum; i++)
{
if (remain_g[temp_point][i] > 0 && !visit[i])
{
q.push(i);
pre[i] = temp_point;
visit[i] = 1;
}
}
}
//更新残留网络
if (pre[end] == 0)//end的前驱没有更新,证明没有增广路径了
break;
int min = INF;
for (int temp_point = end; temp_point != start; temp_point = pre[temp_point])
min = zzMin(remain_g[pre[temp_point]][temp_point], min);
for (int temp_point = end; temp_point != start; temp_point = pre[temp_point])
{
remain_g[pre[temp_point]][temp_point] -= min;
remain_g[temp_point][pre[temp_point]] = min;//此为负向边
}
max_flow += min;
}
return max_flow;
}
对应的最小割问题,在求得最大流后得到残留网络,从s开始进行bfs,并将所有经过的点标记,然后在残留网络中找到连接标记的点和非标记的点的边,经过这些边的直线即为一个最小割.(这些边事实上就已经是满流量了,即流量=容量).1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24//remain_g为找到最大流后得到的残留网络
bool graph::Find_Min_Cut(int start,int remain_g[][1000], vector<int> &s_set, vector<int> &t_set)
{
int visit[1000];
memset(visit, 0, sizeof(visit));
queue<int> s;
s.push(start);
while (!s.empty())
{
int temp_point = s.front();
s.pop();
for (int i = 0; i < Pnum; i++)
if (remain_g[temp_point][i] > 0 && !visit[i])
{
s.push(i);
visit[i] = 1;
s_set.push_back(i);
}
}
for (int i = 0; i < Pnum; i++)
if (!visit[i])
t_set.push_back(i);
return true;
}