> 文章列表 > 最小生成树和最短路径及其他

最小生成树和最短路径及其他

最小生成树和最短路径及其他

还是学过的,主要用于复习q v q

一、最小生成树

最小生成树的定义

用于无向图中,无向图指的是没有带方向路径的图,给定n个点,m条边,如果将这些点依次相连,求出连接这些点的最小数值

应用场景

根据这个算法特性,我们不能猜出此算法主要运用于要在n个城市之间铺设光缆,主要目标是要使这 n 个城市的任意两个之间都可以通信,但铺设光缆的费用很高,且各个城市之间铺设光缆的费用不同,因此另一个目标是要使铺设光缆的总费用最低。这就需要找到带权的最小生成树。

prim算法

 此算法主要是通过选点来进行操作的

 例如此图,用一维数组a来存点,二维数组b来存路径,若从 1 点开始

a[ 2 ] = 2  a[ 3 ] = 4  a[ 4 ] = 7;

选择最小的边的点,就是点 2,那么将 1 ,2 的能连接的边关系为

a[ 3 ] = 1(因为 1 到 3 的路径太长,因此 3 的路径改为 1 )

a[ 5 ] = 2

选择最小边的点,就是点 3,那么将 1 ,2,3 的能连接的边关系为

a[ 4 ] = 1(因为 1 到 4 的路径太长,因此 4 的路径改为 1 )

a[ 5 ] = 2(不变)

因此最终为

 

核心代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>using namespace std;#define INF 0x3f3f3f3f  //超大数int N;  //  储存图
int map[105][105];   //	顶点有没有被访问
bool book[105];     //	存储大圈圈
int lenth[105];
int Prime() 
{memset(book, false, sizeof(book));  //初始化int sum = 0;book[0] = true;//    选中了0这个点,0到n个点的边权for (int i = 1 ; i < N ; i++) {lenth[i] = map[0][i];}for (int i = 1 ; i < N ; i++) {int min = INF;int node;for (int j = 1 ; j < N ; j++) {if (book[j] == false && lenth[j] < min) {//记录最小的边权值min = lenth[j];//记住这个最小的边的下标node = j;}}//得到大圈圈中的最小边,讲其加入到最小边边sum += min;//更新到了的顶点book[node] = true;for (int i = 1 ; i < N ; i++) {if (book[i] == false && lenth[i] > map[node][i]) {lenth[i] = map[node][i];}}cout << i;}return sum;  //返回最短边权和
}int main() 
{while (cin >> N) {for (int i = 0 ; i < N ; i++) {for (int j = 0 ; j < N ; j++) {scanf("%d", &map[i][j]);    //输入邻接矩阵}}cout << Prime() << endl;}return 0;
}

kruskal算法

kruskal算法也没啥好说的了,也就是将边权排序,依次选最小的边,当选的边等于点的数量 - 1的时候就结束,但是在这过程要注意不要围成圈了,因此就需要并查集解决这个问题,那么直接上代码吧

代码模板

#include <bits/stdc++.h>
using namespace std;
int n, m, v, k, ans, fa[10000001];struct node    //定义结构体存图 
{int x;int y;int z;    //z表示x连y的权值 
}stu[100001];//并查集 int find(int x)
{if (x != fa[x]){fa[x] = find(fa[x]);}return fa[x];
}//查找 void unity(int x, int y)
{int r1 = find(x);int r2 = find(y);fa[r1] = r2;
}//合并bool cmp(node a, node b)    //从小到大结构体排序 
{return a.z < b.z;
}int main()
{scanf("%d", &n);for (int i = 1; i <= n; i++){       //并查集初始化fa[i] = i;       }for (int i = 1; i <= n; i++){for (int j = 1; j <= n; j++){scanf("%d", &v);if (j > i)          //邻接矩阵上下对称,存一半就行了 {m++;stu[m].x = i;stu[m].y = j;stu[m].z = v;}}}sort(stu + 1, stu + m + 1, cmp);    //排序 for (int i = 1; i <= m; i++){if (find(stu[i].x) != find(stu[i].y))  //不能连自己{ans += stu[i].z;//加上最小生成树中边的权值 unity(stu[i].x, stu[i].y);//连接起来 k++;//记录边数 if (k == n - 1)//n - 1条边就行了 {printf("%d", ans);return 0;}}}return 0;
}

二、最短路径

最短路径的定义

最短路径一般用于有相同,就是带有方向的路径图

在一个图中,会出现这样的问题,要求你求出一个点到各个点的最短的路径,而这时你可能会想,那这个是不是跟那个最小生成树是不是就是一样的呢?其实不然,最小生成树是连接各个点形成的最小路径,而最短路径则是俩个点之间的最短路径,是不相同的。

迪杰斯特拉算法(dijkstra)

基本思想

首先假定源点为u,顶点集合V被划分为两部分:集合 S 和 V-S。    初始时S中仅含有源点u,其中S中的顶点到源点的最短路径已经确定。
集合S 和V-S中所包含的顶点到源点的最短路径的长度待定,称从源点出发只经过S中的点到达V-S中的点的路径为特殊路径,
并用dist[]记录当前每个顶点对应的最短特殊路径长度。

那么具体的图解我感觉在http://t.csdn.cn/HV7ku

这里面已经写的够清楚了,所以这次就偷个懒,再附赠一张大佬的动态图吧

在这里插入图片描述

 代码模板

#include<stdio.h>
#include<iostream>using namespace std;
#define SIZE 110
#define INF 1000000  //相当于无穷大,判断点与点的连接时需要
int map[SIZE][SIZE];  //邻接矩阵存储
int dis[SIZE];  	//d[i]表示源点到i这个点的距离
int visit[SIZE];  //节点是否被访问
int n, m, j, pos, ans, temp = INF;int dijkstra(int from, int to)   //从源点到目标点
{	int i;for (i = 1; i <= n; i++)  //初始化{	visit[i] = 0;	//一开始每个点都没被访问dis[i] = map[from][i];	//先假设源点到其他点的距离}for (i = 1; i < n; ++i)	  //对除源点的每一个点进行最短计算{				int min = INF;		//记录最小len[i]//记录小len[i] 的点for (j = 1; j <= n; ++j) {if (!visit[j] && min > dis[j]) {pos = j;min = dis[j];}}visit[pos] = 1;for (j = 1; j <= n; ++j) {//如果j节点没有被访问过&&j节点到源节点的最短路径>pos节点到源节点的最短路径+pos节点到j节点的路径if (!visit[j] && (dis[j] > (dis[pos] + map[pos][j]))) { dis[j] = dis[pos] + map[pos][j];	//更新j节点到源节点的最短路径}}}return dis[to];
}int main() {int i, j;//  scanf("%d%d",&n,&m);	//输入数据//测试数据n = 6;	m = 9;for (i = 1; i <= n; ++i) {		//设一开始每个点都不可达for (j = 1; j <= n; ++j) {map[i][j] = INF;}}/*	int a,b,c;	//输入数据for(i = 1 ; i <= m ; ++i){scanf("%d%d%d",&a,&b,&c);map[a][b] = map[b][a] = c;}  *///测试数据map[1][2] = 7;	map[1][3] = 9;map[1][6] = 14;map[2][3] = 10;map[2][4] = 15;map[3][6] = 2;map[5][6] = 9;map[4][5] = 6;map[3][4] = 11;/*	这里的一步完全不用的,多此一举而已,是判断双向边的for (i = 1 ; i <= n ; ++i) {for (j = 1 ; j <= n ; ++j) {if (map[i][j] == temp)map[i][j] = map[j][i];}}*/printf("%d", ans = dijkstra(2, 5));return 0;
}