寒假一个月在家刷了100多道水题,基本的处理已经掌握得差不多了,回学校后开始从图论继续搞。图论这里的东西不难理解,但是写出代码来却很繁杂(多半是受国内数据结构教材的影响),于是在网上搜加上自己修改终于总结了一套我能够理解并掌握的模板,现在拿出来分享给同样挣扎于面对图论题无从下手的童鞋。P.S.:由于考研机试要求不高,这里的算法不做优化了(binary heap or fibonacci heap......),均采用朴素的方法。
一、最小生成树
1.Prim
1 //N至少为顶点数目的上限+1,因为很多题目顶点从1开始编号,这样定义方便一些,就不需要考虑0了
2 #define N 101
3
4 int n, //n为实际的顶点数目,一般从主函数输入,故定义为全局变量
5 adj[N][N], //邻接矩阵
6 visit[N], //记录每个节点的访问情况:0——未访问;1——已访问
7 lowcost[N]; //记录每个顶点与邻接各节点之间的最短距离
8
9 int prim()
10 {
11 memset(visit,0,sizeof(visit));
12 visit[1] = 1;
13 //pos记录当前位置,默认从1号顶点开始
14 int pos = 1;
15 for(int i=2;i<=n;i++)
16 lowcost[i] = adj[pos][i];
17 //ans记录的最小生成树的距离
18 int ans = 0;
19 //因为n个顶点的图的最小生成树有n-1条边,所以我们只需要进行n-1次循环,每次循环添加一条边
20 for(int i=1;i<n;i++)
21 {
22 //这里是将min定义为一个“取不到”的很大的值
23 int min = 0x3f3f3f3f;
24 //寻找与当前访问过的节点邻接的未被访问过且距离最近的节点,重置pos,这个是将要访问的节点
25 for(int j=1;j<=n;j++)
26 if(!visit[j] && lowcost[j] < min)
27 {
28 min = lowcost[j];
29 pos = j;
30 }
31 //添加边
32 ans += min;
33 visit[pos] = 1;
34 //松弛操作,根据pos更新最短长度
35 for(int j=1;j<=n;j++)
36 if(!visit[j] && adj[pos][j] < lowcost[j])
37 lowcost[j] = adj[pos][j];
38 }
39 return ans;
40 }
2.Kruskal + Union-Find Set
1 //N为边的数目的上限
2 #define N 101
3
4 int n, //顶点数
5 m, //边数
6 u[N], //u[i]保存第i条边的始点
7 v[N], //v[i]保存第i条边的终点
8 w[N], //w[i]保存第i条边的权值
9 p[N], //p[i]保存第i条边所在的集合
10 r[N]; //r[i]保存第i条边的编号
11
12 bool cmp(const int i, const int j)
13 {
14 return w[i] < w[j];
15 }
16
17 //找到x所属集合并进行路径压缩
18 int find(int x)
19 {
20 return p[x]==x?x:p[x]=find(p[x]);
21 }
22
23 int kruskal()
24 {
25 for(int i=0;i<n;i++)
26 p[i] = i;
27 for(int i=0;i<m;i++)
28 r[i] = i;
29 //按照长度从小到大对边进行排序
30 sort(r,r+m,cmp);
31 //ans记录最小生成树的距离,cnt记录最小生成树中添加的边数
32 int ans = 0,cnt = 0;
33 for(int i=0;i<m;i++)
34 {
35 //每次从未选择的边中选择距离最短的边
36 int e = r[i];
37 int x = find(u[e]);
38 int y = find(v[e]);
39 //如果u[e]与v[e]不属于同一个集合,说明加入这条边后不会成环
40 if(x != y)
41 {
42 ans += w[e];
43 p[x] = y;
44 cnt++;
45 }
46 }
47 //cnt < n-1说明最小生成树不存在
48 if(cnt < n-1)
49 ans = 0;
50 return ans;
51 }
二、最短路径
1.Dijkstra
1 #define N 101
2 #define INF 0x3f3f3f3f
3
4 int n, //顶点数(顶点编号从1开始)
5 lowcost[N], //记录从起始点到每个顶点的最短距离(不断通过松弛操作更新)
6 visit[N], //记录每个顶点的访问:0——未访问;1——已访问
7 adj[N][N]; //邻接矩阵
8
9 //函数传入int型参数,其值为起始顶点的编号。最后要求从v到e的最短距离,只需输出lowcost[e]即可
10 void dijkstra(int v)
11 {
12 memset(visit,0,sizeof(visit));
13 int pos = v;
14 for(int i=1;i<=n;i++)
15 lowcost[i] = adj[pos][i];
16 lowcost[v] = 0;
17 visit[v] = 1;
18 for(int i=1;i<n;i++)
19 {
20 int min = INF;
21 for(int j=1;j<=n;j++)
22 if(!visit[j] && lowcost[j] < min)
23 {
24 min = lowcost[j];
25 pos = j;
26 }
27 visit[pos] = 1;
28 for(int j=1;j<=n;j++)
29 if(!visit[j] && adj[pos][j] < INF && lowcost[pos] + adj[pos][j] < lowcost[j])
30 lowcost[j] = lowcost[pos] + adj[pos][j];
31 }
32 }
2.Floyd
1 #define N 101
2 #define INF 0x3f3f3f3f
3
4 int n, //顶点数
5 adj[N][N]; //邻接矩阵
6
7 //函数执行结束后,adj[i][j]中存储的就是从i到j的最短路径
8 void floyd()
9 {
10 for(int i=1;i<=n;i++)
11 for(int j=1;j<=n;j++)
12 for(int k=1;k<=n;k++)
13 {
14 if(adj[i][j] != INF && adj[i][k] != INF)
15 {
16 if(adj[i][j] + adj[i][k] < adj[j][k])
17 adj[k][j] = adj[j][k] = adj[i][j] + adj[i][k];
18 }
19 }
20 }
3.SPFA
1 #define N 101
2 #define INF 0x3f3f3f3f
3
4 int n, //顶点数(顶点编号从1开始)
5 lowcost[N], //记录从起始点到每个顶点的最短距离(不断通过松弛操作更新)
6 visit[N], //记录每个顶点的访问:0——未访问;1——已访问
7 adj[N][N], //邻接矩阵
8 queue[N*N]; //每个顶点至多入队N次,否则说明图中存在负环,而这是SPFA算法无法处理的。
9
10 //函数传入int型参数,其值为起始顶点的编号
11 void spfa(int s)
12 {
13 memset(visit,0,sizeof(visit));
14 memset(queue,0,sizeof(queue));
15 int front = 0, rear = 1;
16 for(int i=1;i<=n;i++)
17 lowcost[i] = INF;
18 queue[front] = s;
19 visit[s] = 1;
20 lowcost[s] = 0;
21 //循环条件为队列非空
22 while(front < rear)
23 {
24 int t = queue[front];
25 visit[t] = 0;
26 //每取一次队首顶点,对所有顶点进行一次松弛操作
27 for(int i=1;i<=n;i++)
28 if(lowcost[t] + adj[t][i] < lowcost[i])
29 {
30 lowcost[i] = lowcost[t] + adj[t][i];
31 if(!visit[i])
32 {
33 visit[i] = 1;
34 queue[rear++] = i;
35 }
36 }
37 }
38 front++;
39 }
40 }
以后还会补充其他模板。。。