最短路模版整理

接着前一篇,整理完最小生成树的模版,接下来整理一下最短路的模版。

所有题目都是根据HDU 1874 写的。


图论中求最短路的算法比较多,有Dijkstra、Bellman-ford、Floyd-Warshall、以及SPFA。主要用到的就是这么几个算法。下面一一介绍。


一、Dijkstra

这个算法的做法就是,每次找到一个距源点距离最近的点,然后扩展这个与这个点相邻的点的距离。 这个算法只能针对非负权图进行求单源最短路,算法复杂度是O(V^2)(不优化),但是可以通过堆优化(我就用优先队列写写吧。。)复杂度可以降到O(V+ElogV)

未优化模版:

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int INF = 0x3f3f3f3f;
struct edge{
    int u,v,next,w;
}e[1005];
int head[205],cnt;
int d[205],vis[205];
int n,m;
void addedge(int u,int v,int w){
    e[cnt].u = u;
    e[cnt].v = v;
    e[cnt].w = w;
    e[cnt].next = head[u];
    head[u] = cnt++;
}
int Dijkstra(int s,int t){
    memset(d,0x3f,sizeof(d));
    memset(vis,0,sizeof(vis));
    d[s] = 0;
    for(int i=0;i<n;i++){
        int k = -1,minn = INF;
        for(int j=0;j<n;j++)
            if(!vis[j]&&d[j]<minn)
                minn = d[j],k = j;
        if(k == -1)break;
        vis[k] = 1;
        for(int j = head[k];j!=-1;j = e[j].next){
            int v = e[j].v,w = e[j].w;
            if(!vis[v]&& d[v]>d[k]+w)
                d[v] = d[k]+w;
        }
    }
    if(d[t] == INF)return -1;
    return d[t];
}
int main()
{
    while(scanf("%d%d",&n,&m)==2){
        int u,v,w;
        memset(head,-1,sizeof(head));
        cnt = 0;
        while(m--){
            scanf("%d%d%d",&u,&v,&w);
            addedge(u,v,w);
            addedge(v,u,w);
        }
        scanf("%d%d",&u,&v);
        printf("%d\n",Dijkstra(u,v));
    }
    return 0;
}


对于找最小值的过程,可以用堆优化,为了方便,用优先队列实现。

模版如下

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
const int INF = 0x3f3f3f3f;
typedef pair<int ,int > PII;
struct edge{
    int u,v,next,w;
}e[1005];
int head[205],cnt;
int d[205],vis[205];
int n,m;
void addedge(int u,int v,int w){
    e[cnt].u = u;
    e[cnt].v = v;
    e[cnt].w = w;
    e[cnt].next = head[u];
    head[u] = cnt++;
}
int Dijkstra(int s,int t){
    memset(d,0x3f,sizeof(d));
    memset(vis,0,sizeof(vis));
    d[s] = 0;
    priority_queue <PII,vector<PII>,greater<PII> >q;
    q.push(make_pair(d[s],s));
    while(!q.empty()){
        PII t = q.top();
        q.pop();
        vis[t.second] = 1;
        int k = t.second;
        for(int j = head[k];j!=-1;j = e[j].next){
            int v = e[j].v,w = e[j].w;
            if(!vis[v]&&d[v]>d[k]+w)
                d[v] = d[k]+w,q.push(make_pair(d[v],v));
        }
    }
    if(d[t] == INF)return -1;
    return d[t];
}
int main()
{
    while(scanf("%d%d",&n,&m)==2){
        int u,v,w;
        memset(head,-1,sizeof(head));
        cnt = 0;
        while(m--){
            scanf("%d%d%d",&u,&v,&w);
            addedge(u,v,w);
            addedge(v,u,w);
        }
        scanf("%d%d",&u,&v);
        printf("%d\n",Dijkstra(u,v));
    }
    return 0;
}

二、Bellman-Ford

这个算法思想特别神奇。将起点的距离设为0,其他点设为INF,对于每一条边都做一次松弛操作。总共进行n-1次这样的操作。这样之后,dis数组里存的就是s到各点的最短距离。其实每次操作都只是更新了前一次更新的点,所以进行了很多不必要的扫描。SPFA正是把这些不必要的操作去掉得到的优化。

Bellman-Ford(普通做法 复杂度O(VE))

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int INF = 0x3f3f3f3f;
struct edge{
    int u,v,w;
}e[1005];
int n,m,cnt;
int d[205];
int Bellman_ford(int s){
    memset(d,0x3f,sizeof(d));
    d[s] = 0;
    for(int i=1;i<n;i++)
        for(int j=1;j<=cnt;j++)
            d[e[j].v] = min(d[e[j].v],d[e[j].u]+e[j].w);
    for(int i=1;i<=cnt;i++)
        if(d[e[i].v]>d[e[i].u]+e[i].w)return 0;
    return 1;
}
int main()
{
    while(scanf("%d%d",&n,&m)==2){
        int u,v,w,st,ed;
        cnt = 0;
        for(int i=1;i<=m;i++){
            scanf("%d%d%d",&u,&v,&w),e[++cnt].u = u,e[cnt].v = v,e[cnt].w = w;
            e[++cnt].u = v,e[cnt].v = u,e[cnt].w = w;
        }
        scanf("%d%d",&st,&ed);
        int ok = Bellman_ford(st);
        if(ok && d[ed]!=INF)printf("%d\n",d[ed]);
        else puts("-1");
    }
    return 0;
}

SPFA(好用,好写,复杂度小)


#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>
using namespace std;
const int INF = 0x3f3f3f3f;
struct edge{
    int u,v,w,next;
}e[1005];
int head[205],d[205],inq[205],cnt,n,m;
void addedge(int u,int v,int w){
    e[cnt].u = u;
    e[cnt].v = v;
    e[cnt].w = w;
    e[cnt].next = head[u];
    head[u] = cnt++;
}
int SPFA(int s){
    memset(inq,0,sizeof(inq));
    memset(d,0x3f,sizeof(d));
    d[s] = 0;
    queue<int>q;
    q.push(s);
    while(!q.empty()){
        int t = q.front();
        q.pop();
        inq[t] = 0;
        for(int i=head[t];i!=-1;i = e[i].next){
            int v = e[i].v,w = e[i].w;
            if(d[v]>d[t]+w){
                d[v] = d[t]+w;
                if(!inq[v])inq[v] = 1,q.push(v);
            }
        }
    }
    for(int i=0;i<cnt;i++)
        if(d[e[i].v]>d[e[i].u]+e[i].w)return 0;
    return 1;
}
int main()
{
    while(scanf("%d%d",&n,&m)==2){
        int u,v,w,st,ed;
        cnt = 0;
        memset(head,-1,sizeof(head));
        while(m--){
            scanf("%d%d%d",&u,&v,&w);
            addedge(u,v,w),addedge(v,u,w);
        }
        scanf("%d%d",&st,&ed);
        int ok = SPFA(st);
        if(ok&&d[ed]!=INF)printf("%d\n",d[ed]);
        else puts("-1");
    }
    return 0;
}

三、 Floyd-Warshall

这个算法非常好写,原理基于动态规划,就不细说了。。能求出任意两点间的最短路,必须使用邻接矩阵存图,复杂度是O(n^3)

模版

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int INF = 0x3f3f3f3f;
int g[205][205];
int n,m;
int main()
{
    while(scanf("%d%d",&n,&m)==2){
        int u,v,w;
        memset(g,0x3f,sizeof(g));
        for(int i=1;i<=m;i++){
            scanf("%d%d%d",&u,&v,&w);g[u][v] = g[v][u] = min(g[u][v],w);}
        int st,ed;
        scanf("%d%d",&st,&ed);
        for(int i=0;i<n;i++)g[i][i] = 0;
        for(int k=0;k<n;k++)
            for(int i=0;i<n;i++)
                for(int j=0;j<n;j++)
                    if(g[i][j]>g[i][k]+g[k][j])
                        g[i][j] = g[i][k]+g[k][j];
        if(g[st][ed] == INF)g[st][ed] = -1;
        printf("%d\n",g[st][ed]);
    }
    return 0;
}

算法很多,但是要把这些算法都学以致用很不容易。大概以后每道题都要想想多解,才能把这些图论算法的适用范围和自己的优势给弄清楚吧。还差了很多火候。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值