最短路(floyd+bellman+dijkstra+dijkstra优先队列优化+spfa)

本文深入讲解了五种经典的图算法,包括Floyd-Warshall、Bellman-Ford、SPFA、Dijkstra及其优化版本。每种算法都配有详细的实现代码,并通过一个实际问题展示了它们的应用场景。
摘要由CSDN通过智能技术生成

最短路种类 时间复杂度作用局限性

floyd-Warshell O(n^3) 求多源最短路 ,能解决负权边,有向无向图皆可; 时间复杂度太大,不能解决负权环情况;


bellman-ford O(V*E) 求单源最短路,能解决负权边,能解决负权回路并能输出,有向无向图皆可;没有很明显的局限性;


spfa 最坏O(V*E),一般很快求单源最短路,能解决负权边,能解决负权回路不能输出,有向无向图皆可;没有很冥想局限性且一般快于bellman-ford


dijkstra O(n*n)求单源最短路,有向图无向图皆可;不能解决负权边及负权回路;


dijkstra+优先队列优化 (总之非常快,只略逊于对优化) 求单源最短路,有向图无向图皆可;不能解决负权边及负权回路;


下面以hdu2544(超级大水模板题)展示各种算法:

floyd-warshell算法:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 100+10;
const int inf = 0x3f3f3f3f;
int gra[maxn][maxn];
int main()
{
    int n,m;
    while(scanf("%d%d",&n,&m)==2&&n&&m)
    {
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
            if(i==j) gra[i][j]=0;
            else gra[i][j]=inf;//以上4行是初始化;
        int a,b,t;
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d%d",&a,&b,&t);
            gra[a][b]=gra[b][a]=t;
        }
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                for(int k=1;k<=n;k++)
                if(gra[j][k]>gra[j][i]+gra[i][k])
                gra[j][k]=gra[j][i]+gra[i][k];//以上5行是floyd-warshell算法核心语句;
        printf("%d\n",gra[1][n]);
    }
    return 0;
}


bellman-ford算法:

如果要判断负环的话加一个cnt数组就行,放置松弛操作的if复合语句里,输出最短路径的话在松弛操作的if复合语句里;

#include<bits/stdc++.h>
using namespace std;
const int maxn = 100+10;
const int inf = 0x3f3f3f3f;//相加也不会超int范围;
int u[20000+10],v[20000+10],w[20000+10];
int dis[maxn];
int main()
{
    int n,m;
    while(scanf("%d%d",&n,&m)==2&&n&&m)
    {
        for(int i=1;i<=n;i++) dis[i]=inf;
        dis[1]=0;//以上两行是初始化;
        int pos=1;
        int a,b,t;
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d%d",&a,&b,&t);
            u[pos]=a,v[pos]=b,w[pos]=t;
            pos++;
            u[pos]=b,v[pos]=a,w[pos]=t;
            pos++;
        }//因为是无向图;
        for(int i=1;i<=n;i++)
            for(int j=1;j<pos;j++)
            {
                if(dis[v[j]]>dis[u[j]]+w[j])
                dis[v[j]]=dis[u[j]]+w[j];
            }//以上四行是核心语句;
        printf("%d\n",dis[n]);
    }
    return 0;
}

bellman-ford算法+队列优化

邻接表实现:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 100+10;
const int inf = 0x3f3f3f3f;
int head[maxn],vis[maxn],dis[maxn];
struct Edge
{
    int to,t,next;
};
Edge edge[20000+10];
int pos;
void addedge(int a,int b,int t)
{
    edge[pos].next=head[a];
    edge[pos].to=b;
    edge[pos].t=t;
    head[a]=pos++;
}//核心操作;
int main()
{
    int n,m;
    while(scanf("%d%d",&n,&m)==2&&n&&m)
    {
        for(int i=1;i<=n;i++)
        {
            vis[i]=0;
            head[i]=-1;
            dis[i]=inf;
        }
        dis[1]=0,pos=0,vis[1]=1;//以上五行初始化;
        int a,b,t;
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d%d",&a,&b,&t);
            addedge(a,b,t);
            addedge(b,a,t);
        }
        queue<int>q;
        q.push(1);
        while(!q.empty())
        {
            int x=q.front();q.pop();vis[x]=0;
            for(int i=head[x];i!=-1;i=edge[i].next)
            {
                int y=edge[i].to;
                int z=edge[i].t;
                if(dis[y]>dis[x]+z)
                {
                    dis[y]=dis[x]+z;
                    q.push(y);
                    vis[y]=1;//(*);
                }
            }
        }//这个while循环就是核心;
        printf("%d\n",dis[n]);
    }
    return 0;
}

vector实现(之后补充)

dijkstra

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
using namespace std;
const int inf = 0x3f3f3f3f;
const int maxn = 100+10;
int dis[maxn],vis[maxn],gra[maxn][maxn];
int n,m;


int main()
{
    while(scanf("%d%d",&n,&m)==2&&n&&m)
    {
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
            if(i==j) gra[i][j]=0;
            else gra[i][j]=inf;
        int a,b,t;
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d%d",&a,&b,&t);
            gra[a][b]=gra[b][a]=t;
        }
        for(int i=1;i<=n;i++)
        {
            vis[i]=0;
            dis[i]=gra[1][i];
        }
        for(int i=1;i<=n;i++)
        {
            int maxx=inf,x;
            for(int j=1;j<=n;j++)
            {
                if(!vis[j]&&maxx>=dis[j])
                {
                    maxx=dis[j];
                    x=j;
                }
            }
            vis[x]=1;
            for(int j=1;j<=n;j++)
            {
                if(dis[j]>dis[x]+gra[x][j])
                    dis[j]=dis[x]+gra[x][j];
            }
        }
        printf("%d\n",dis[n]);
    }
    return 0;
}

dijkstra+优先队列优化(邻接表实现)

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
using namespace std;
typedef pair<int,int> pii;
const int inf = 0x3f3f3f3f;
const int maxn = 100+10;
int head[maxn];
int vis[maxn];
int dis[maxn];
struct Edge
{
    int to,t,next;
};
Edge edge[20000+10];
int n,m;
int pos;
void addedge(int a,int b,int t)
{
    edge[pos].next=head[a];
    edge[pos].to=b;
    edge[pos].t=t;
    head[a]=pos++;
}
int main()
{
    while(scanf("%d%d",&n,&m)==2&&n&&m)
    {
        for(int i=1;i<=n;i++)
        {
            head[i]=-1;
            vis[i]=0;
            dis[i]=inf;
        }
        pos=0;dis[1]=0;
        int a,b,t;
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d%d",&a,&b,&t);
            addedge(a,b,t);
            addedge(b,a,t);
        }
        priority_queue<pii,vector<pii>,greater<pii> > pq;//pair默认先比较第一个元素;
        pq.push(pii(0,1));
        while(!pq.empty())
        {
            pii temp=pq.top();pq.pop();
            int t1=temp.first;
            int t2=temp.second;//后面没有括号,不要混淆;
            if(vis[t2]) continue;
            vis[t2]=1;
            for(int i=head[t2];i!=-1;i=edge[i].next)//i一直都是标号;
            {
                int z=edge[i].to,zz=edge[i].t;
                if(dis[z]>dis[t2]+zz)
                {
                    dis[z]=dis[t2]+zz;
                    pq.push(pii(dis[z],z));//此处用pq.push(make_pair(dis[z],z))也可;
                }
            }
        }
        printf("%d\n",dis[n]);
    }
    return 0;
}//注意标号从零开始和点从1开始别混淆;

vector实现(有空补充);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值