1 描述
求从指定源点出发到各个顶点的最短路径。
假设:图中结点名均为单个互不相同的字母,权值均>0。
输入:
第一行:结点数量n,弧数量e,源点
后续e行:<结点,结点,权值>
输出:
按结点的升序输出到达各个结点的最短路径长度
测试输入 | 期待的输出 | 时间限制 | 内存限制 | 额外进程 | |
---|---|---|---|---|---|
测试用例 1 | 以文本方式显示
| 以文本方式显示
| 1秒 | 64M | 0 |
测试用例 2 | 以文本方式显示
| 以文本方式显示
| 1秒 | 64M | 0 |
测试用例 3 | 以文本方式显示
| 以文本方式显示
| 1秒 | 64M | 0 |
2 解题
<1> floyd算法
- 可以先看这个帖子熟悉一下 Floyd - Warshall(弗洛伊德算法)
- 简单的来说就是三层迭代
- cpp代码
/*
* @Author: 鱼香肉丝没有鱼
* @Date: 2021-11-11 21:38:14
* @Last Modified by: 鱼香肉丝没有鱼
* @Last Modified time: 2021-11-11 23:07:32
*/
#include <algorithm>
#include <climits>
#include <iostream>
#include <string>
using namespace std;
int vexnum;
int edgenum;
char sourceNode;
int** matrix = NULL; //存储图的二维数组
//输入字母,返回数字
int CtoI(char ch)
{
return ch - 'a' + 1;
}
char ItoC(int i)
{
return i - 1 + 'a';
}
//使用邻接矩阵来存储这个图
void CreateGraph()
{
int i, j;
char start;
char end;
int weight;
//申请内存
matrix = new int*[vexnum + 1];
for(i = 0; i <= vexnum; i++) {
matrix[i] = new int[vexnum + 1];
}
//初始化
for(i = 1; i <= vexnum; i++) {
for(j = 1; j <= vexnum; j++)
matrix[i][j] = INT_MAX; //都设为无穷大
matrix[i][i] = 0; //自己到自己的距离是0
}
//读取用户输入
int k = 1;
while(k <= edgenum) {
scanf("<%c,%c,%d>", &start, &end, &weight);
getchar();
//转换一下
i = CtoI(start);
j = CtoI(end);
matrix[i][j] = weight; //有向图
k++;
}
}
void Floyd()
{
int i, j, k;
for(k = 1; k <= vexnum; k++) {
for(i = 1; i <= vexnum; i++) {
for(j = 1; j <= vexnum; j++) {
if(matrix[i][k] == INT_MAX || matrix[k][j] == INT_MAX)
continue; //如果已经有了无穷大,那么应该跳过,不然相加会溢出导致错误
if(matrix[i][j] > matrix[i][k] + matrix[k][j])
matrix[i][j] = matrix[i][k] + matrix[k][j];
}
}
}
}
//输出这个邻接矩阵
void Output()
{
for(register int i = 1; i <= vexnum; i++) {
for(register int j = 1; j <= vexnum; j++) {
cout << matrix[i][j] << " ";
}
cout << endl;
}
}
//输出以源点到各个顶点的距离,这时候已经是最短距离了,只需要输出这一行就行了
void Print(int i)
{
for(register int j = 1; j <= vexnum; j++) {
cout << ItoC(j) << ":" << matrix[i][j] << endl;
}
}
int main()
{
// freopen("file in.txt", "r", stdin);
scanf("%d,%d,%c", &vexnum, &edgenum, &sourceNode);
getchar(); //吸收后面的换行符,这是用scanf读取字符的时候需要注意的一点
CreateGraph();
Floyd();
// Output();
Print(CtoI(sourceNode));
return 0;
}
<2> Bellman-Ford算法
- 相关知识 图-贝尔曼福特(BELLMAN-FORD)算法详解
- 我是用邻接表来存储的,也可以用边集数组,但是我懒得写了
- 代码
/*
* @Author: 鱼香肉丝没有鱼
* @Date: 2021-11-12 08:22:23
* @Last Modified by: 鱼香肉丝没有鱼
* @Last Modified time: 2021-11-12 11:06:31
*/
#include <algorithm>
// #include <climits>//不使用INT—MAX,后面涉及加减运算,会导致数据溢出
#include <cstdbool>
#include <iostream>
#include <string>
#define MAX 99999999;
using namespace std;
typedef struct edgelist
{
int to; //终点
int next; //指向的是兄弟的下标
int weight; //权重
} EDGE;
EDGE* E = NULL; //数组。邻接表
int cnt = 0;
int* head = NULL; //数组 链式前向星
//顶点数量
// 边的数量
// 源点字母
int vexnum;
int edgenum;
char sourceNode;
int* dis; //源点到各个顶点的距离
int* parent; //保存顶点的直接前驱,这个题中并未使用
//输入字母,返回数字
int CtoI(char ch)
{
return ch - 'a';
}
char ItoC(int i)
{
return i + 'a';
}
//初始化邻接矩阵
void Initial(int* head)
{
register int i;
for(i = 0; i < vexnum; i++) {
head[i] = -1;
}
}
//使用一维数组存储邻接表
void CreateEdgeList(int i, int j, int weight)
{
E[cnt].to = j;
E[cnt].weight = weight;
E[cnt].next = head[i];
head[i] = cnt++;
}
//使用邻接矩阵来存储这个图,下标从0开始
void CreateGraph()
{
int i, j;
char start;
char end;
int weight;
//申请内存
E = new EDGE[edgenum];
head = new int[vexnum];
//初始化head[]
Initial(head);
//读取用户输入
int k = 1;
while(k <= edgenum) {
scanf("<%c,%c,%d>", &start, &end, &weight);
getchar();
//转换一下
i = CtoI(start);
j = CtoI(end);
CreateEdgeList(i, j, weight); //有向图
k++;
}
}
void Bellman_Ford(int begin)
{
int i, j;
int current = 0;
dis = new int[vexnum]; //起点到每个顶点的距离
parent = new int[vexnum];
for(i = 0; i < vexnum; i++) {
dis[i] = MAX; //干开始的时候为无限大
parent[i] = -1; //刚开始的时候都没有直接前驱,都设为0
}
dis[begin] = 0; //自己到自己的距离为0
int k = vexnum - 1;
while(k--) { //因为只是一遍循环会导致后面扫描到的更短的路线不会更新到最后终点,需要遍历vexnum-1次
for(i = 0; i < vexnum; i++) { //确保遍历了vexnum个顶点
current = (i + begin) % vexnum; //要从begin开始然后每一个顶点连过的边都要走过
for(j = head[current]; j != -1; j = E[j].next) {
if(dis[E[j].to] > dis[current] + E[j].weight) {
dis[E[j].to] = dis[current] + E[j].weight;
parent[E[j].to] = current;
}
}
}
}
}
//判断有没有负权回路,如果还能进行松弛操作,那么说明有
bool NegativeCycle()
{
for(int i = 0; i < vexnum; i++) {
for(int j = head[i]; j != -1; j = E[j].next) {
if(dis[E[j].to] > dis[i] + E[j].weight) //只要有一个能够进行松弛操作
return true;
}
}
return false;
}
//输出这个邻接表
void PrintGraph()
{
// printf("the ALGraph is\n");
for(register int i = 0; i < vexnum; i++) {
printf("%c", ItoC(i));
for(register int j = head[i]; j != -1; j = E[j].next) {
printf("-->%c", ItoC(E[j].to));
}
printf("\n");
}
}
// 输出以源点到各个顶点的距离,这时候已经是最短距离了
void Print()
{
for(register int i = 0; i < vexnum; i++) {
cout << ItoC(i) << ":" << dis[i] << endl;
}
}
int main()
{
freopen("file in.txt", "r", stdin);
scanf("%d,%d,%c", &vexnum, &edgenum, &sourceNode);
getchar(); //吸收后面的换行符,这是用scanf读取字符的时候需要注意的一点
CreateGraph();
// PrintGraph();
Bellman_Ford(CtoI(sourceNode)); //这个算法只计算输入源点到各顶点的距离
// bool flag=NegativeCycle();//判断有没有负回路
// cout<<flag<<endl;
Print();
return 0;
}
- 代码实现的功能还有判断有无负权回路
- 每个顶点的直接前驱也保存了,但是并没有使用
<3> Dijikstra算法
- 先贴一个简单的原理链接 Dijkstra算法原理
- 代码还没写,先占个坑
- 代码如下
插入代码片