最短路径前两种算法实现 例题 sdut oj2143

本文介绍了图结构中最短路径问题的两种经典算法——弗洛伊德算法和迪杰斯特拉算法,并提供了这两种算法的详细解释及C++实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

图结构练习——最短路径

Time Limit: 1000MS Memory limit: 65536K

题目描述

 给定一个带权无向图,求节点1到节点n的最短路径。
 

输入

 输入包含多组数据,格式如下。
第一行包括两个整数n m,代表节点个数和边的个数。(n<=100)
剩下m行每行3个正整数a b c,代表节点a和节点b之间有一条边,权值为c。
 

输出

 每组输出占一行,仅输出从1到n的最短路径权值。(保证最短路径存在)
 

示例输入

3 2
1 2 1
1 3 1
1 0

示例输出

1
0

提示

 两种算法,弗洛伊德时间复杂度为O(N^3)
                迪杰斯特拉时间复杂度为O(N^2)

示例程序


算法一:
   弗洛伊德算法:Floyed-Warshall
根据经验,如果要让任意两个顶点之间的路程变短,只能引入
第三个点通过这个点中转求得。
假如中转点是顶点K,只需判断mp[i][k]+mp[k][j]是否比mp[i][j]小即可。
基本思想是:最开始只允许经过一个顶点进行中转,接下来++i实现
从i号顶点到j号顶点只经过前k号点的最短路径。
Floyd算法完整代码:


#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;

int main()
{
    int n, m;
    int mp[112][112];
    while(cin >> n >> m)
    {
        for(int i = 1; i <= n; i++)
        {
            for(int j = 1; j <= n; j++)
            {
                if(i == j)
                    mp[i][j] = 0;
                else
                    mp[i][j] = INF;
            }
        }
        while(m--)
        {
            int a, b, c;
            cin >> a >> b >> c;
            if(mp[a][b] > c)
                mp[a][b] = mp[b][a] = c;
        }
        Floyd  弗洛伊德算法核心语句:
        for(int i = 1; i <= n; i++)
        {
            for(int j = 1; j <= n; j++)
            {
                if(mp[i][j] != INF)
                {
                    for(int k = 1; k <= n; k++)
                    {
                        mp[j][k] = min(mp[j][k], mp[j][i]+mp[i][k]);
                    }
                }
            }
        }
//        for(int k = 1; k <= n; k++)
//        {
//            for(int i = 1; i <= n; i++)
//            {
//                for(int j = 1; j <= n; j++)
//                {
//                    if(mp[i][k] < INF&&mp[k][j] < INF&&mp[i][j] > mp[i][j]+mp[k][j])
//                        mp[i][j] = mp[i][k]+mp[k][j];
//                }
//            }
//        }
        cout << mp[1][n] << endl;
    }
    return 0;
}

算法二:
迪杰斯特拉 Dijkstra -- 单源最短路
此算法的基本思想是:每次找到离源点最近的顶点,然后以该顶点为
            中心进行扩展,(松弛)最终得到源点到其余所有点的
            最短路径。
此算法基本步骤如下:
1:
   将所有的顶点分为两部分:已知最短路程的顶点集合P和未知最短
路径的顶点集合Q。最开始,集合P中只有源点一个顶点。我们这里用
一个book数组来记录那些点在集合P中。例如对于某个顶点i,如果
book[i]为1则表示这个顶点在集合P中,如果book[i]为0则表示这个顶
点在集合Q中。
2:
    设置源点s到自己的最短路径为0即dis[s]=0。若存在有源点能直
接到达的顶点i,则把dis[i]设为mp[s][i]。同时把所有其他(源点不
能直接到达的)顶点的最短路径设为无穷。
3:
    在集合Q的所有顶点中选择一个离源点s最近的顶点u(即dis[u]最小)
加入的集合P。并考察所有以点u为起点的边,对每一条边进行松弛操作。
例如存在一条从u到v的边,那么可以通过将边u->v添加到尾部来拓展一
条从s到v的路径,这条路径的长度是dis[u]+mp[u][v]。如果这个值比目
前一致的dis[v]的值要小,我们可以用新值来替代当前dis[v]中的值。
4:
    重复第3步,如果集合Q为空,算法结束。最终dis数组中的值就是
源点到所有定点的最短路径。


完整的Dijkstra算法代码:

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f

        using namespace std;

int mp[110][110];
int book[110], dis[110];
int Min, u;
int main()
{
    int m, n;
    while(cin >> n >> m)
    {
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= n; j++)
            {
                if(i == j)
                    mp[i][j] = 0;
                else
                    mp[i][j] = INF;
            }
        while(m--)
        {
            int a, b, c;
            scanf("%d %d %d", &a, &b, &c);
            if(mp[a][b] > c)
            {
                mp[a][b] = mp[b][a] = c;
            }
        }
        for(int i = 1; i <= n; i++)
            dis[i] = mp[1][i];
        for(int i = 1; i <= n; i++)
            book[i] = 0;
        book[1] = 1;
        Dijkstra算法核心语句:
        for(int i = 1; i <= n-1; i++)
        {
            Min = INF;
            for(int j = 1; j <= n; j++)
            {
                if(!book[j]&&dis[j] < Min)
                {
                    Min = dis[j];
                    u = j;
                }
            }
            book[u] = 1;
            for(int v = 1; v <= n; v++)
            {
                if(mp[u][v] < INF)
                {
                    dis[v] = min(dis[v], dis[u]+mp[u][v]);
                }
            }
        }
        printf("%d\n", dis[n]);
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值