最短路径《一》(以杭电1874为例)

本文介绍了几种常见的单源最短路径算法,包括Floyd、Dijkstra、Bellman-ford和SPFA算法,并提供了每种算法的实现代码,有助于读者理解和掌握这些算法的基本原理和应用场景。

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

几种比较常用的单源(所用点到某一确定点的距离)最短路径的算法有:Dijkstra、Floyd、SPFA、Bellman-ford等。

Floyd算法的思想就是动态规划,用三层循环,来不断的去更新两个点之间的距离,虽然比较简单,但是时间复杂度是O(n^3),适合应用数据比较小的题型。

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<math.h>
using namespace std;
#define Max 10000
int ma[1005][1005];
void floyd(int n){//三种循环遍历一遍
     int i, k, j;
     for(k = 0; k < n; k++){
        for(i = 0; i < n; i++){
            for(j = 0; j < n; j++){
                ma[i][j] = min(ma[i][j], ma[i][k] + ma[k][j]);
            }
        }
     }
}
int main(){
    int n, m;
    while(~scanf("%d %d", &n, &m)){
        int i, j;
        for(i = 0; i < n; i++){
            for(j = 0; j < n; j++)
                ma[i][j] = Max;
            ma[i][i] = 0;
        }
        for(i = 0; i < m; i++){
            int t1, t2, t3;
            cin>>t1>>t2>>t3;
            if(ma[t1][t2] > t3)
                ma[t1][t2] = ma[t2][t1] = t3;
        }
        floyd(n);
        int st, en;
        cin>>st>>en;
        if(ma[st][en] == Max)cout<<"-1"<<endl;
        else cout<<ma[st][en]<<endl;
    }
return 0;
}

Dijkstra算法的思想就是用一个先用数组dis去储存所有点到某点m的距离,然后找到最小的一个,再dis更新所有点到最小点的距离加上最小点到m的距离,把已经用到的标记上,证明已经用过了,然后接着找最小的一个。

#include<stdio.h>
#include<iostream>
#include<string.h>
using namespace std;
#define INF 0x3f3f3f3f
int ma[300][300];
int dis[300], vis[300];
int n, m;
void Dijkstra(int st){
     int i, j;
     memset(vis, 0, sizeof(vis));//vis用来标记
     for(i = 0; i < n; i++)
        dis[i] = ma[st][i];//所用点到初始点m的距离
     vis[st] = 1;
     dis[st] = 0;
     for(i = 1; i < n; i++){//遍历n-1次
        int sign, mi = INF;
        for(j = 0; j < n; j++){//找到目前
            if(dis[j] < mi && !vis[j]){
                mi = dis[j];
                sign = j;
            }
        }
        vis[sign] = 1;
        for(j = 0; j < n; j++){//更新
            if(dis[j] > dis[sign] + ma[sign][j])
                dis[j] = dis[sign] + ma[sign][j];
        }
     }


}
int main(){
    int i, j;
    while(~scanf("%d %d", &n, &m)){
        for(i = 0; i < 300; i++){
            for(j = 0; j < 300; j++)
                ma[i][j] = INF;
            ma[i][i] = 0;
        }

        for(i = 0; i < m ; i++){
            int t1, t2, v;
            scanf("%d %d %d", &t1, &t2, &v);
            if( v < ma[t1][t2])
            ma[t1][t2] = ma[t2][t1] = v;
        }
        int st, en;
        scanf("%d %d", &st, &en);
        Dijkstra(st);
        printf("%d\n",dis[en]==INF?-1:dis[en]);


    }
return 0;
}


用优先队列优化:

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<queue>
using namespace std;
#define Max 1<<30
struct P{
      int pos;
      int val;
      friend bool operator <(P A, P B){//按照从小到大的顺序
             return A.val > B.val;
      }
};

int ma[1005][1005], dis[1005];
int vis[1005];
int n, m;
void Dijkstra(int st){
    priority_queue<P>que;
     memset(vis, 0, sizeof(vis));
     for(int i = 0; i < n; i++){
        dis[i] = Max;
     }
     dis[st] = 0;//初始点
     P f;
     f.pos = st, f.val = 0;
     que.push(f);//加入队列
     while(!que.empty()){
        P p = que.top();
        que.pop();
        int v = p.pos;
        if(vis[v])continue;
        vis[v] = 1;
        for(int i = 0; i < n; i++){//更新
            if(!vis[i] && dis[i] > dis[v] + ma[v][i]){
                dis[i] = dis[v] + ma[v][i];
                P f;
                f.pos = i;
                f.val = dis[i];
            que.push(f);
          }
        }
     }
}
int main(){
    while(~scanf("%d %d", &n, &m)){
        for(int i = 0; i < n; i++){
            for(int j = 0; j < n; j++)
                ma[i][j] = Max;
            ma[i][i] = 0;
        }
        for(int i = 0; i < m; i++){
            int t1, t2, t3;
            cin>>t1>>t2>>t3;
            if(ma[t1][t2] > t3)
                ma[t1][t2] = ma[t2][t1] = t3;
        }
        int st, en;
        cin>>st>>en;
        Dijkstra(st);
        if(dis[en] == Max)cout<<"-1"<<endl;
        else cout<<dis[en]<<endl;
    }

return 0;
}

Bellman-ford算法思想就是松弛两点间的距离。进行n-1便对所有点进行松弛。

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<math.h>
using namespace std;
#define Max 1000000
int dis[1005];
struct edge{
       int from, to, val;
}e[1005];
int n, m;
void bellman_floyd(int st){
     int i, j;
     for(i = 0; i < n; i++)dis[i] = Max;
     dis[st] = 0;
     for(i = 0; i < n; i++){
        for(j = 0; j < m; j++){
            if(dis[e[j].to] > dis[e[j].from] + e[j].val){
               dis[e[j].to] = dis[e[j].from] + e[j].val;

            }
            if(dis[e[j].from] > dis[e[j].to] + e[j].val){
               dis[e[j].from] = dis[e[j].to] + e[j].val;

            }
        }
     }
}
int main(){

    while(~scanf("%d %d", &n, &m)){
        int i, j;
        for(i = 0; i < m; i ++){
            int t1, t2, t3;
            cin>>t1>>t2>>t3;
            e[i].from = t1;
            e[i].to = t2;
            e[i].val = t3;
        }
        int st, en;
        cin>>st>>en;
        bellman_floyd(st);
        if(dis[en] == Max)cout<<"-1"<<endl;
        else cout<<dis[en]<<endl;
    }
return 0;
}

SPFA算法其实就是对Bellman-ford算法的优化,该算法的思想就是从从第一个点开始压出队列,然后跟新到这个压出点的距离,释放这个点,更新的点如果在这个队列里面,就不需要压入,如果没有重新压入。

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<math.h>
#include<queue>
using namespace std;
#define Max 1000000
int dis[1005];
int vis[1005];
int ma[1005][1005];
int n, m;
queue<int>q;
void SPFA(int st)
{
    int i, j;
    for(i = 0; i < n; i++)
    {
        dis[i] = Max;
        vis[i] = 0;
    }
    vis[st] = 1;
    dis[st] = 0;
    q.push(st);
    while(!q.empty())
    {
        int t = q.front();
        vis[t] =  1;
        q.pop();
        for(i = 0; i < n; i++)
        {
            if(dis[i] > dis[t] + ma[t][i])
            {
                dis[i] = dis[t] + ma[t][i];
                if(!vis[i])
                {
                    vis[i] = 1;
                    q.push(i);
                }
            }

        }
        vis[t] = 0;

    }

}
int main()
{
    while(~scanf("%d %d", &n, &m))
    {
        int i, j;
        for(i = 0; i < n; i++)
        {
            for(j = 0; j < n; j++)
                ma[i][j] = Max;
            ma[i][i] = 0;
        }
        for(i = 0; i < m; i++)
        {
            int t1, t2, t3;
            cin>>t1>>t2>>t3;
            if(ma[t1][t2] > t3)
                ma[t1][t2] = ma[t2][t1] = t3;
        }

        int st, en;
        cin>>st>>en;
        SPFA(st);
        if(dis[en] == Max)cout<<"-1"<<endl;
        else cout<<dis[en]<<endl;
    }
    return 0;
}

优化:使用vector来储存数据

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<vector>
#include<queue>
using namespace std;
#define Max 1000000
int dis[1000], vis[1000];
struct Node
{
    int to, val;
} k;
vector<Node>g[1000];
queue<int>q;
int n, m, i, j;
void SPFA(int st)
{
    for(i = 0; i < n; i++)
    {
        dis[i] = Max;
        vis[i] = 0;
    }
    dis[st] = 0;
    vis[st] = 1;
    q.push(st);
    while(!q.empty())
    {
        int v = q.front();
        vis[v] = 1;
        q.pop();
        for(i = 0; i < g[v].size(); i++)
        {
            if(dis[g[v][i].to] > dis[v] + g[v][i].val)
            {
                dis[g[v][i].to] = dis[v] + g[v][i].val;
                if(!vis[g[v][i].to])
                {
                    vis[g[v][i].to] = 1;
                    q.push(g[v][i].to);
                }
            }
        }
        vis[v] = 0;
    }

}
int main()
{
    while(~scanf("%d %d", &n, &m))
    {
        for(i = 0; i < n; i++)g[i].clear();
        int t1, t2, t3;
        for(i = 0; i < m; i++)
        {
            cin>>t1>>t2>>t3;
            k.to = t2;
            k.val = t3;
            g[t1].push_back(k);
            k.to = t1;
            k.val = t3;
            g[t2].push_back(k);
        }
        int s, t;
        cin>>s>>t;
        SPFA(s);
        if(dis[t] == Max)cout<<"-1"<<endl;
        else cout<<dis[t]<<endl;
    }
    // for(i = 0; i < n; i++)cout<<dis[i]<< " ";
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值