7-4 最短路径之Dijkstra(朴素dijkstra打印路径)

作者 龚雄兴

单位 湖北文理学院

本题目要求通过读入无向网的边的信息(省略了各顶点的信息,仅用顶点编号来表示),构造图,并利用Dijkstra算法,求出指定源点到其它各点的最短路径。

样例">输入样例:

第一行,两个整数,顶点数vN和边数eN。
以后若干行,是相关边的信息,无向图的边是对称的,只输入一半的边(小编号到大编号的,间以空格),最后两行各一个整数,前一个指定源点,后一个指定的查询的终到点。
(注意,示例中34条边,只输入了17条边的信息)

10 34
0 1 2
0 3 5
1 2 5
1 3 2
2 4 8
2 5 4
3 5 4
3 6 2
4 7 5
4 5 2
5 6 3
5 7 9
5 8 7
6 8 7
7 8 3
7 9 4
8 9 8
0
8

输出样例:

在一行中输出从源点到指定终点的短路径及代价,注意:所有符号均为西文符号。

0-->1-->3-->6-->8:13

个人思路:

朴素dijkstra+打印路径

dijkstra算法思路:

dijkstra求的是单源最短路问题

int g[i][j]  : 从点i 到 点j 的距离

int dist[N] :  从起点到每一个点的最短距离

bool  st[N] : 当前是否确定了最短路

1.初始化距离

将起点初始化为0 ,其他所有点都等于正无穷(一个比较大的数)

2.for循环N次

每次找到未确定最短路且距离最近的点 t 。

接下来 st [ t ] = true;

然后用 点t 更新其他点的距离。

看一下 从点t 出发到达的每个点到起点的距离,能不能用起点到点t再到这个点的距离更新。

即假设 点t 能到达 点x ,判断dist[x] 是否小于dist[t] + g[t][x],小于即更新dist[x]。

最后我们得到的dist数组即为每个点到起点的最短距离

3.打印路径

考虑dijkstra算法特点,每次我们选出一个当前的最短距离点t ,用它去更新其余点,如果有点被更新,那么这个点当前的最短路径中,点t 一定是这个点的前一个点,根据这个特点,我们可以用一个path数组,保存这个点的前一个节点,当所有点的最短路径都确定以后,我们的path数组保存的刚好是每个点的最短路的前一个节点,因为dijkstra是单源最短路,path保存每个点最短路径的前一个点,而前一个点的最短路径必定包含在后一个的最短路径里面。因此他们可以连起来。此时,我们利用一个循环,便可将最短路径倒序输出。

#include <bits/stdc++.h>
using namespace std;

const int N = 100;
const int INF = 100000;
int n, m;


int g[N][N];   //利用邻接矩阵保存两个点的距离
int dist[N];   //保存每个点到起点的最短距离
bool st[N];    //每个点是否已经确定最短距离
int path[N];   //每个点最短路径的前一个节点
int dijkstra(int sec,int n)
{
    memset(dist, 0x3f, sizeof dist); //初始化所有距离为最大
    dist[sec] = 0;                   //初始化起点距离为0
    for (int i = 0; i < n - 1; i++)  //循环找到起点最短的且未确定最短距离的点t
    {
        int t = -1;     
        for (int j = 0; j <= n; j++)
            if (!st[j] && (t == -1 || dist[t] > dist[j])) {
                t = j;
            }
               
        for (int j = 0; j <= n; j++) {
            if (dist[j] > dist[t] + g[t][j]) {
                path[j] = t; //若从起点到点j的距离被从起点到点t,再从点t到点j更新,
                             //那么t就是j的前驱节点,保存到path[j]里
            }
            dist[j] = min(dist[j] , dist[t] + g[t][j]);//更新最短路
        }
            

        st[t] = true;   //点t的最短路径已经确定
    }

    if (dist[n] == 0x3f3f3f3f) return -1;
    return dist[n];
}



int main() {
    memset(g, 0x3f, sizeof g);     //初始化邻接矩阵距离无穷大
    memset(path, -1, sizeof path); //初始化每个点的前驱为-1,表示没有前驱
    cin >> n >> m;
	m /= 2;
	while (m--) {
		int a, b, c;
		scanf("%d%d%d", &a, &b, &c);
        //由于是无向图,需要加入两个边,a到b,和b到a
		g[a][b] = min(g[a][b], c);
		g[b][a] = min(g[b][a], c);
	}
	int s, e;  // s为起点,e为终点
	scanf("%d", &s);
	scanf("%d", &e);
    if(s==e){
        cout<<s<<"-->"<<e<<":0";
        return 0;
    }
    int ret = dijkstra(s, n-1); //传入起点,和点的个数,由于点从0开始,所以应该传入n-1
    
    stack<int> stk;  //根据path特点,我们用一个栈保存,最后弹栈输出时,即为正序
    int k = e;
    while (true) {
        if (k == s) break;
        stk.push(path[k]);
        k = path[k];
    }
    while (!stk.empty()) {
        cout << stk.top() << "-->";
        stk.pop();
    }
    cout << e;
    printf(":%d", dist[e]);
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

星河边采花

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值