28. 求单点的最短路径

1 描述

求从指定源点出发到各个顶点的最短路径。

图

假设:图中结点名均为单个互不相同的字母,权值均>0。

输入:
第一行:结点数量n,弧数量e,源点
后续e行:<结点,结点,权值>

输出:
按结点的升序输出到达各个结点的最短路径长度

 测试输入 期待的输出 时间限制 内存限制 额外进程
测试用例 1以文本方式显示
  1. 7,10,a↵
  2. <a,b,13>↵
  3. <a,c,8>↵
  4. <c,d,5>↵
  5. <d,e,6>↵
  6. <a,e,30>↵
  7. <a,g,32>↵
  8. <b,g,7>↵
  9. <b,f,9>↵
  10. <e,f,2>↵
  11. <f,g,17>↵
以文本方式显示
  1. a:0↵
  2. b:13↵
  3. c:8↵
  4. d:13↵
  5. e:19↵
  6. f:21↵
  7. g:20↵
1秒64M0
测试用例 2以文本方式显示
  1. 5,9,a↵
  2. <a,b,10>↵
  3. <b,a,20>↵
  4. <a,d,50>↵
  5. <a,e,45>↵
  6. <b,c,15>↵
  7. <c,d,20>↵
  8. <c,e,5>↵
  9. <d,e,10>↵
  10. <e,c,30>↵
以文本方式显示
  1. a:0↵
  2. b:10↵
  3. c:25↵
  4. d:45↵
  5. e:30↵
1秒64M0
测试用例 3以文本方式显示
  1. 5,7,a↵
  2. <a,b,10>↵
  3. <b,c,50>↵
  4. <a,d,30>↵
  5. <a,e,100>↵
  6. <c,e,10>↵
  7. <d,c,20>↵
  8. <d,e,60>↵
以文本方式显示
  1. a:0↵
  2. b:10↵
  3. c:50↵
  4. d:30↵
  5. e:60↵
1秒64M0

2 解题

<1> floyd算法

/*
 * @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算法

/*
 * @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算法

插入代码片
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值