单源最短路径算法

来源:互联网 发布:水平垂直居中 知乎 编辑:程序博客网 时间:2024/04/29 11:03
#include <iostream>#include <cstdio>#include <memory.h>#include <queue>using namespace std;struct edge{    //边    int from;   //起点    int to;     //终点    int cost;   //花费};const int MAX_E = 10000;   //最大边数const int MAX_V = 10000;   //最大顶点数const int INF = 10000;     //最大距离edge es[MAX_E];             //边集合int V,E;                    //V是顶点数,E是边数int d[MAX_V];               //最短距离//求出从顶点s到其余顶点的最短距离//该算法服从如下递推关系式://d[i] = min{d[j]+(从j到i的边的权值)|e=(j,i)∈E}//设初值,d[s] = 0,d[i] = INF//该方法仅适用于无负圈的有向图void shortpath_bellman_ford(int s){ //bellman-ford算法    for(int i = 0; i < V; ++i){ //初始化顶点距离为INF        d[i] = INF;    }    d[s] = 0;    while(true){        bool update = false;        for(int i = 0; i < E; ++i){ //遍历所有边            edge e = es[i];            while(d[e.from] != INF && d[e.to] > d[e.from] + e.cost){  //如果起点不为无穷大且已有边距离大于 始边+权值的距离,则更新距离                d[e.to] = d[e.from] + e.cost;                update = true;            }        }        if(!update){ //如果循环一圈无更改值,则以求出最短距离            break;        }    }}//最短路径中,不会有经过同一个顶点两次,所有在没有负圈的情况下最多更新V-1次bool find_negative_loop(){  //判断是否有负圈,返回true有,false没有    memset(d, 0, sizeof(d));    for(int i = 0; i < V; ++i){        for(int j = 0; j < E; ++j){            edge e = es[j];            if(d[e.to] > d[e.from] + e.cost){                d[e.to] = d[e.from] + e.cost;                //如果第V次还更新了,则证明有负圈                if(i == V - 1){                    return true;                }            }        }    }    return false;}int cost[MAX_V][MAX_V]; //cost[u][v]表示边e=(u,v)的权值,(不存在时,用INF表示)bool used[MAX_V];       //已用顶点,表示在确定最短路径的那个集合中//不能有负边//该算法的核心思想如下://从起点开始,算出起点到每个点的最短距离,不断加入距离最短的顶点,修改从起点到每个点的最短距离,直至所有顶点加入//以下实现算法,在第一次循环的时候会算出起点s到每一个顶点的最短距离void shortpath_dijkstra(int s){ //求从起点s出发到各个顶点的最短距离    fill(d, d + V, INF);        //距离数组d初始化为INF    fill(used, used + V, false);//访问数组used初始化为false    d[s] = 0;   //s到自己的距离设置为0    while(true){        int v = -1;     //v表示距离(已使用顶点最短距离)的顶点        //从未使用过的顶点中,找出距离最短的顶点        for(int i = 0; i < V; ++i){            if(!used[i] && (v == -1 || d[i] < d[v])){                v = i;            }        }        if(v == -1){    //如果循环一次未找到未使用过的,距离最短的值,则表示算法结束            break;        }        used[v] = true; //置v为已使用顶点        //更新从v->i的最短路径        for(int i = 0; i < V; ++i){            //取原有距离 或者 加入新顶点之后的距离 的最小值            d[i] = min(d[i],d[v] + cost[v][i]);        }    }}typedef pair<int, int> P;   //first是最短距离,second是顶点的编号vector<edge> G[MAX_V];      //邻接表表示的图//不能有负边//该算法为dijkstra的优化算法//使用邻接表和最小堆void shortpath_dijkstra_optimize(int s){     ////求从起点s出发到各个顶点的最短距离    //实现最小堆,并且按P中的first从小到大取值    priority_queue<P, vector<P>, greater<P> > que;  //最小堆    fill(d, d + V, INF);    d[s] = 0;    que.push(P(0, s));    while(!que.empty()){    //当堆不为空时        P p = que.top();    //取出堆中最小值        que.pop();          //弹出最小值        int v = p.second;   //取出距离最短边对应的编号        //在访问的时候因为顶点更新,会造成同一个顶点,可能被多次加入最小堆中        if(d[v] < p.first)  //如果取出距离大于最短距离则忽略            continue;        for(int i = 0; i < G[v].size(); i++) {   //更新该顶点到每一个邻接点的距离            edge e = G[v][i];            if(d[e.to] > d[v] + e.cost) {   //如果加入v点后,到i的点距离变小                d[e.to] = d[v] + e.cost;    //更新距离                que.push(P(d[e.to], e.to)); //压入这点的距离和该点的编号            }        }    }}int main(){////    //测试//    //测试数据//    /*//    IN//    4 5 3//    0 1 2//    1 2 3//    2 3 5//    3 0 1//    3 1 4//    OUT//    1 3 6 0//    *///    cin >> V >> E;  //输入顶点数和边数//    int s;  //起始点//    cin >> s;//    for(int i = 0; i < E; ++i){//        cin >> es[i].from >> es[i].to >> es[i].cost;   //输入边属性//    }//   shortpath_bellman_ford(s);//    for(int i = 0; i < V; ++i){//        cout << d[i] << " ";//    }////    //测试数据//    /*//    3 4 0//    0 1 -2//    1 2 3//    2 0 -2//    0 2 4//    OUT//    1//    *///    cout << find_negative_loop() << endl;    //测试数据    /*    IN    7 10 0    0 1 2    0 2 5    1 2 4    1 3 6    1 4 10    2 3 2    3 5 1    4 5 3    4 6 5    5 6 9    OUT    0 2 5 7 11 8 16    *///    cin >> V >> E;  //输入顶点数和边数//    int s;  //起始点//    cin >> s;//    for(int i = 0; i < V; ++i){//        for(int j = 0; j < V; ++j){//            cost[i][j] = INF;//        }//    }//    for(int i = 0; i < E; ++i){//        cin >> es[i].from >> es[i].to >> es[i].cost;   //输入边属性//        cost[es[i].from][es[i].to] = es[i].cost;//        cost[es[i].to][es[i].from] = es[i].cost;//    }//    shortpath_dijkstra(s);//    for(int i = 0; i < V; ++i){//        cout << d[i] << " ";//    }    //测试数据    /*    IN    4 0    3    1 1    2 2    3 5    2    0 1    3 2    2    0 2    3 3    3    0 5    1 2    2 3    OUT    0 1 3 3    */    cin >> V;    int s;  //起始点    cin >> s;    edge e; //临时边    for(int i = 0; i < V; ++i){        cin >> E;   //输入邻接边数        for(int i = 0; i < E; ++i){            cin >> e.to >> e.cost;   //输入边属性            G[i].push_back(e);        }    }    shortpath_dijkstra_optimize(s);    for(int i = 0; i < V; ++i){        cout << d[i] << " ";    }    return 0;}

0 0
原创粉丝点击