最短路模版整理

来源:互联网 发布:淘宝3c数码配件 编辑:程序博客网 时间:2024/05/22 12:44

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

所有题目都是根据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;}

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


0 0
原创粉丝点击