最短路

来源:互联网 发布:手机淘宝怎样添加好友 编辑:程序博客网 时间:2024/06/05 18:02

Dijkstra算法

朴素的Dijkstra算法和prim算法在代码上惊人的相似。

而Dijkstra算法的思想是:在待扩展边中取出最小边,利用此边进行松弛操作,然后更新待扩展边集。重复以上步骤直到所有结点都已访问过。

Dijkstra算法只对没有负权回路的图有效,当然对带负权边,但没有负权回路的图依然有效。

for (int i=1;i<n;i++){    minn=INF;    for (int j=1;j<=n;j++)    if (!vis[j] && d[j]<minn)        minn=d[k=j];    vis[k]=true;    for (int j=1;j<=n;j++)        d[j]=min(d[j],d[k]+map[k][j]);}

明显朴素的Dijkstra算法耗时O(N^2),时间主要浪费在每次循环寻找待扩展边中的最小边,利用C++的STL提供的方便,使用优先队列进行优化,当然图的存储方法只能改为使用边表存储,否则松弛操作一样耗时巨大。下面的代码写得一般,有点长了。

#include <iostream>#include <cstdio>#include <cstring>#include <queue>#include <vector>using namespace std;struct edge{     //存储边    int v,w;    edge() { v=w=0; }    edge(int x,int y) { v=x; w=y; }};vector<edge> a[100];struct node{         //存储待扩展边的数据,当然也可以直接利用存储边的结构体,但是为了直观就分开写了    int dd,w;     //dd存储结点,w存储边权    node() {dd=w=0; };    node(int x,int y) { dd=x;w=y; }    bool operator < (const node &x) const      //优先队列比较函数    {        if (w==x.w) return dd<x.dd;        else return w<x.w;    }};int d[100];int dis[100];void dijkstra(int S){    priority_queue<node> p;    memset(dis,10,sizeof(dis));    dis[S]=0;    p.push(node(S,dis[S]));     //把start点压入队列    while (!p.empty())    {        node u=p.top(); p.pop();    //弹出待扩展边的最小边        for (int i=0;i<d[u.dd];i++)    //松弛操作        {            edge e=a[u.dd][i];            if (dis[e.v]>u.w+e.w)            {                dis[e.v]=u.w+e.w;                p.push(node(e.v,dis[e.v]));  //待扩展边进队            }        }    }}int main(){    for (int i=0;i<100;i++) a[i].clear();    memset(d,0,sizeof(d));    int n,m,u,v,w;    cin>>n>>m;    for (int i=1;i<=m;i++)    {        cin>>u>>v>>w;        a[u].push_back(edge(v,w));        d[u]++;    }    dijkstra(1);    for (int i=1;i<=n;i++)        cout<<"1->"<<i<<":"<<dis[i]<<endl;    return 0;}

 

Bellman-Ford算法

朴素的bellman-ford算法说死了就是一个笨方法,就是通过不断地对每一条边进行松弛操作,耗时O(VE),但是经过一个简单的优化后效率大大提升,听说效率比Dijkstra好多了。

这里直接给出经过优化的bellman-ford算法。

#include <iostream>#include <cstdio>#include <cstring>#include <vector>using namespace std;#define NN 105struct edge{    int u,v,w;    edge() {u=v=w=0;}    edge(int x,int y,int z) {u=x;v=y;w=z;}};vector <edge> e;int n;int dis[NN];bool relax(int u,int v,int w)  //松弛操作{    if (dis[u]+w<dis[v])    {        dis[v]=dis[u]+w;        return true;    }    return false;}bool Bell(int S){    memset(dis,10,sizeof(dis));    dis[S]=0;    bool flag=false;    int count=0;    while (!flag && ++count<n)  //注意flag的变化,其实flag就是优化,不用flag则运行n-1次也会停下来    {        for (int i=0;i<e.size();i++)            flag=flag||relax(e[i].u,e[i].v,e[i].w);  //如果没有进行过松弛操作,那么就没必要在做下去了        flag=!flag;    }    for (int i=0;i<e.size();i++)   //负权回路的判断        if (relax(e[i].u,e[i].v,e[i].w))            return false;    return true;}int main(){    int m,u,v,w;    cin>>n>>m;    e.clear();    for (int i=1;i<=m;i++)    {        cin>>u>>v>>w;        e.push_back(edge(u,v,w));        e.push_back(edge(v,u,w));    }    bool ff=Bell(1);    if (!ff)        cout<<"no"<<endl;    else for (int i=2;i<=n;i++)            cout<<dis[i]<<endl;    return 0;}

 

SPFA算法

SPFA算法是西南交大段凡丁发表的,在bellman-ford的基础上加入了队列优化,而在SPFA算法上又可以加入SLF和LLL优化,使得整个算法的效率非常可观。

算法的思想是从队列中取出点进行松弛操作,若松弛成功则加入队列,直到队列为空。而为了处理负权回路,需要加入一个数组记录结点入队的次数,若一个结点入队次数大于n,则可以判断出现了负权回路。

这里并没有加入SLF和LLL优化。

#include <iostream>#include <cstdio>#include <cstring>#include <vector>#include <queue>using namespace std;#define NN 1000struct edge{    int v,w;    edge() {v=w=0;}    edge(int x,int y) {v=x;w=y;}};vector<edge> e[NN];int dis[NN];int n;bool relax(int u,int v,int w)    //松弛操作{    if (dis[u]+w<dis[v])    {        dis[v]=dis[u]+w;        return true;    }    return false;}bool SPFA(int S){    memset(dis,10,sizeof(dis));    dis[S]=0;    int count[NN]={0};    //统计入队次数    count[S]=1;    queue<int> q;    q.push(S);    bool f[NN]={0};    //标记是否在队列中,避免重复进队    f[S]=true;    while (!q.empty())    {        int u=q.front();        for (int i=0;i<e[u].size();i++)        {            int v=e[u][i].v;            if (count[v]>=n) return false;            if (!f[v] && relax(u,v,e[u][i].w))            {                count[v]++;                q.push(v);                f[v]=true;            }        }        q.pop();        f[u]=false;    }    return true;}int main(){    int m,u,v,w;    cin>>n>>m;    for (int i=1;i<=m;i++)    {        cin>>u>>v>>w;        e[u].push_back(edge(v,w));        e[v].push_back(edge(u,w));  //无向图    }    bool ff=SPFA(1);    if (!ff) cout<<"no"<<endl;    else for (int i=2;i<=n;i++)            cout<<dis[i]<<endl;    return 0;}

 

Floyd算法

Floyd算法最简单但是速度不敢恭维。

思想:对每个节点不断进行松弛操作,到最后得到的结果就是多源最短路的结果了。

for (int k=1;k<=n;k++)    for (int i=1;i<=n;i++)        for (int j=1;j<=n;j++)            map[i][j]=min(map[i][j],map[i][k]+map[k][j]);
原创粉丝点击