码迷,mamicode.com
首页 > 编程语言 > 详细

单源最短路径算法

时间:2018-07-22 23:32:10      阅读:227      评论:0      收藏:0      [点我收藏+]

标签:防止   溢出   ext   ++   pat   ati   表示   原理   百度   

目录

主要参考算法导论

基本性质

使用min_w(s,v)表示源节点s到v的最短路径长度;
w(u,v)表示节点u到v的权重;
u.d表示源节点s到节点u的当前路径长度;

松弛操作

relax(u,v,w)
{
    if(u.d + w < v.d)
    {   
        v.d = u.d + w;
    }   
} 

三角不等式

    min_w(s,v) <= min_w(s,u) + w(u,v);

路径松弛性质

    如果p(v0, v1, v2, ...... , vk)为v0到vk 的最短路径,并且对边的松弛次序为(v0,v1),(v1,v2),(v2,v3),......(vk-1,vk),则松弛过后u.d = min_w(s,v);

Bellman Ford算法

Bellman 算法解决的是一般情况下的单源最短路径问题,边的最短路可以为负值
适用于:存在环路,路径权重为负值的情况,但图中不可包含权重为负值的环路。

时间复杂度

O(VE);外层循环为O(V),内层循环使用聚合分析得O(E);总的复杂度为:O(VE);

代码:

#include <iostream>
#include <algorithm>
#include <queue> 
#include <vector>
#include <stack>
using namespace std;
#define INF 0x7fffffff
struct Edge{
    int u,v,t;
};
int N,T;
Edge a[2002];
int ans[1001];
int pre[1001];
void bellman_ford(int s)
{
    for(int i = 1; i <= N; ++i)
    {
        ans[i] = INF;
    }
    pre[s] = 0;
    ans[s] = 0;
    int e1 = N - 1;//循环次数为顶点数减一;因为一条最短路径最多包含 N -1 条边; 
    int e2 = T;//边数; 
    for(int i = 0 ; i < e1; ++i)
    {
        for(int j = 0; j < e2; ++j)
        {
            //以下是对边的松弛操作,为了防止数据溢出,进行了多次判断。
            //实际原理很简洁:
            /*
                int tmp = ans[a[j].u] + a[j].t;
                if(tmp < ans[a[j].v])
                {
                    ans[a[j].v] = tmp;//更新对短路路径值; 
                    pre[a[j].v] = a[j].u;//更新前驱节点; 
                }
            */ 
            if(ans[a[j].u] == INF)continue;
            int tmp = ans[a[j].u] + a[j].t;
            if(ans[a[j].v] == INF)
            {
                ans[a[j].v] = tmp;
                pre[a[j].v] = a[j].u;
            }
            else if(tmp < ans[a[j].v])
            {
                ans[a[j].v] = tmp;
                pre[a[j].v] = a[j].u;
            }
        }
    }
}
int main()
{
    cin >> T >> N;
    for(int i = 0; i < T; ++i)
    {
        cin >> a[i].u >> a[i].v >> a[i].t;
    }
    bellman_ford(1);
        //输出所有的最短路径;
    for(int i = 1; i <= N; ++i)
    {
        int id = i;
        stack<int> st;
        while(pre[id] != 0)
        {
            st.push(id);
            id = pre[id];
        }
        st.push(id);
        cout << st.top();
        st.pop();
        while(!st.empty())
        {
            int tmp = st.top();
            st.pop();
            cout << "-->" <<tmp;
        }
        cout << endl;
    }
    return 0;   
}
/*
10 5
1 2 6
1 5 7
2 3 5
2 5 8
3 2 -2
4 3 7
4 1 2
5 1 7
5 3 -3
5 4 9
*/

spfa(Shortest Path Faster Algorithm) 算法

使用队列对Bellman ford进行优化:
Bellman ford是对每个边进行松弛,实际上,每一次只有再上一次松弛中最短的路径的值(d)发生改变的点可达的点才有松弛的可能,
所以,使用一个队列保存,d值发生改变点,然后依次对队列中的点,相关边进行松弛。

算法的正确性:

假定存在一条最短路径: 如果p(v0, v1, v2, ....vk,vk+1,.. , vM)为v0到vM 的最短路径,
使用路径松弛定理进行证明,容易知道若某一次while循环松弛了边(vk-1,vk),由于vk入队,则后续循环中当vk出队,则(vk,vk+1)边必会得到松弛,所以算法是正确的。

时间复杂度:

O(k*E),k的取值2-3(参考百度百科)

代码

#include <iostream>
#include <algorithm>
#include <queue> 
#include <vector>
#include <stack>
using namespace std;
#define INF 0x7fffffff
struct Ed{
    int to,cost;
    Ed(){
    }
    Ed(int a,int b):to(a),cost(b)
    {
    }
};
int N,T;
vector<Ed> a[1001];
int ans[1001];
int pre[1001];
int vis[1001];
void spfa(int s)
{
    for(int i = 1; i <= N; ++i)
    {
        ans[i] = INF;
    }
    pre[s] = 0;
    ans[s] = 0;
    vis[s] = 1;
    queue<int> q;
    q.push(s);
    while(!q.empty())
    {
        int now = q.front();
        q.pop();
        vis[now] = 0;
        int len = a[now].size();
        for(int i = 0; i < len; ++i)
        {
            int nt = a[now][i].to;
            int wt = a[now][i].cost;
            int tmp = ans[now] + wt;
            if(tmp < ans[nt])
            {
                ans[nt] = tmp;
                pre[nt] = now;
                if(vis[nt] == 0)
                {
                    q.push(nt);
                    vis[i] = 1;
                }
            }
        }
    }
}
int main()
{
    cin >> T >> N;
    int uu,vv,tt;
    for(int i = 0; i < T; ++i)
    {
        cin >> uu >> vv >> tt;
        a[uu].push_back(Ed(vv,tt));
    }
    spfa(1);
        //输出所有的最短路径;
    for(int i = 1; i <= N; ++i)
    {
        int id = i;
        stack<int> st;
        while(pre[id] != 0)
        {
            st.push(id);
            id = pre[id];
        }
        st.push(id);
        cout << st.top();
        st.pop();
        while(!st.empty())
        {
            int tmp = st.top();
            st.pop();
            cout << "-->" <<tmp;
        }
        cout << endl;
    }
    return 0;   
}
/*
10 5
1 2 6
1 5 7
2 3 5
2 5 8
3 2 -2
4 3 7
4 1 2
5 1 7
5 3 -3
5 4 9
*/

Dijkstra 算法

Dijkstra 算法解决的是带权图的有向图的最短路径问题,要求w(u,v) >= 0;

伪代码:

集合S为顶点最短路径值已知的点; 
集合V为所有顶点的集合;  
Dijkstra(int s){
    1.初始化;将所有点的最短路径估计值设为INF; 
    2.将源节点的最短路径值设为0.
    当S != V 时,执行如下操作: 
        1.在 V - S中找到最短路径估计值最小的顶点u,加入集合S. 
        2.对 u 的所有相邻节点的进行松弛操作Relax. 
}

算法导论伪代码:
    集合S为顶点最短路径值已知的点; 
    集合V为所有顶点的集合; 
    最小优先队列Q用于保存集合 V - S;
     
Dijkstra(int s){
    1.初始化;将所有点的最短路径估计值设为INF; 
    2.将源节点的最短路径值设为0.
    while Q != empty
        u = Extract_min(Q); 在 Q中找到最短路径估计值最小的顶点u
        S = S U {u};
        for 节点 u 的每个可达节点 v
            Relax(u,v,w); 
}

时间复杂度

    O((V+E)lg(V));{主要参考算法导论}

代码实现

#include <iostream>
#include <algorithm>
#include <queue>
#include <vector>
using namespace std;
#define N 201
#define INF 0x7fffffff
struct Ed{
    int to,cost;//分别表示到达的下一个节点的序号和边的权重;
    Ed(int a,int b):to(a),cost(b)
    {
    }
    Ed()
    {
    }
};
struct Nod{
    int id,d;
    Nod(int id_,int d_):id(id_),d(d_)
    {
    }
    Nod(){
        
    }
};
struct cmp{//用于优先队列中节点优先级的比较;
    bool operator()(Nod&a,Nod&b){
        return a.d > b.d;
    }
};
vector<Ed> a[N];
int ans[N];
void dijkstra(int s)
{
    for(int i = 1; i <= N; ++i)
    {
        ans[i]  = INF; 
    } 
    priority_queue<Nod,vector<Nod>,cmp> q;
    ans[s] = 0;
    q.push(Nod(s,0));
    while(!q.empty())
    {
        Nod now = q.top();
        q.pop();
        int id = now.id;
        int d = now.d;
        if(ans[id] < d)continue;//发现节点在进入优先队列后又被进行了松弛;直接跳过;
        int len = a[id].size();
        int nt,w;
        for(int i = 0; i < len; ++i)
        {
            nt = a[id][i].to;
            w = a[id][i].cost;
            int tmp = d + w;
            if(tmp < ans[nt])
            {
                ans[nt] = tmp;
                q.push(Nod(nt,tmp));
            }
        }
    }

}

例题练习

Til the Cows Come Home
Frogger
Heavy Transportation
更多练习

单源最短路径算法

标签:防止   溢出   ext   ++   pat   ati   表示   原理   百度   

原文地址:https://www.cnblogs.com/lif323/p/9340153.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!