eoj1817 dijkstra单元最短路径 普通方法+二叉堆优化

求出有n(1 < n < 600)个结点有向图中,结点1到结点n的最短路径。

Input
第一行有2个整数n和m(0 < m <= n*(n-1)/2),接下来m行每行有三个整数u,v,w结点u到v之间有一条权为w的边(w<1000000)。

Output
输出结点1到结点n之间的最短路径,如果1到n之间不存在路径,输出 -1。

Sample Input
3 3
1 2 10
2 3 15
1 3 30

 

 

 

题目分析:dijkstra单元最短路径。

一.最短路径的最优子结构性质

该性质描述为:如果P(i,j)={Vi….Vk..Vs…Vj}是从顶点i到j的最短路径,k和s是这条路径上的一个中间顶点,那么P(k,s)必定是从k到s的最短路径。下面证明该性质的正确性。

   假设P(i,j)={Vi….Vk..Vs…Vj}是从顶点i到j的最短路径,则有P(i,j)=P(i,k)+P(k,s)+P(s,j)。而P(k,s)不是从k到s的最短距离,那么必定存在另一条从k到s的最短路径P'(k,s),那么P'(i,j)=P(i,k)+P'(k,s)+P(s,j)<P(i,j)。则与P(i,j)是从i到j的最短路径相矛盾。因此该性质得证。

二.Dijkstra算法

 

   由上述性质可知,如果存在一条从i到j的最短路径(Vi…..Vk,Vj),Vk是Vj前面的一顶点。那么(Vi…Vk)也必定是从i到k的最短路径。为了求出最短路径,Dijkstra就提出了以最短路径长度递增,逐次生成最短路径的算法。譬如对于源顶点V0,首先选择其直接相邻的顶点中长度最短的顶点Vi,那么当前已知可得从V0到达Vj顶点的最短距离dist[j]=min{dist[j],dist[i]+matrix[i][j]}。根据这种思路,

假设存在G=<V,E>,源顶点为V0,U={V0},dist[i]记录V0到i的最短距离,path[i]记录从V0到i路径上的i前面的一个顶点。

1.从V-U中选择使dist[i]值最小的顶点i,将i加入到U中;

2.更新与i直接相邻顶点的dist值。(dist[j]=min{dist[j],dist[i]+matrix[i][j]})

3.知道U=V,停止。

 

三,重用邻接矩阵实现,这对于稀疏图来说效率往往会很低,常用的优化方法有1,用邻接表存储,2用二叉堆优化每次选择最短路径的速度。下面分别给出两种代码。

 

 

 

版本一:常用邻接矩阵实现方法:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <string>
#include <vector>
#include <map>
#include <algorithm>

using namespace std;

int cost[605][605],low[605];//cost为邻接矩阵,low为当前到源点的最短距离
bool vis[605];

const int maxn=1000000;

int main()
{
    int n,m,i,j,k;
    memset(vis,false,sizeof(vis));
    while(~scanf("%d%d",&n,&m)){
        for(i=0;i<605;++i)
            for(j=0;j<605;++j)
                cost[i][j]=maxn;
        for(i=0;i<m;++i){
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            cost[a][b]=c;
        }

        for(i=1;i<=n;++i){
            low[i]=cost[1][i];
            vis[i]=false;
        }
        vis[1]=true;
        for(i=2;i<=n;++i){
            int minn=1<<30;
            for(j=1;j<=n;++j){//选择当前最近点,这里可用堆优化
                if(!vis[j] && low[j]<minn){
                    minn=low[j];
                    k=j;
                }
            }
            vis[k]=true;//将当前的加入以最优集合,之后不再对这点判断
            for(j=1;j<=n;++j){//更新
                if(!vis[j] && low[k]+cost[k][j]<low[j]){
                    low[j]=low[k]+cost[k][j];
                }
            }
        }
        if(low[n]>=maxn)
            printf("-1\n");
        else
            printf("%d\n",low[n]);
    }
    return 0;
}

版本二:使用二叉堆和邻接表,对大数据的稀疏图效果更好。

#include <iostream>
#include <cstdio>
#include <queue>
#include <vector>
#include <cstring>
using namespace std;
const int MAXN=1000000;
vector<int> g[605];//邻接表,g[i]一次存储与i邻接的边的下标,方便快速查找
bool vis[605];//标记数组
int low[605],n,m;//当前最短距离
struct Edge{//边类
    int from,to,dis;
};
vector<Edge> edge;//存储边的情况
struct Node{//用于优先队列中的节点
    int d,u;
    bool operator<(const Node &b)const{
        return this->d>b.d;
    }
};
void dijkstra(){
    for(int i=0;i<=n;++i) low[i]=MAXN;//初始化
    low[1]=0;
    memset(vis,false,sizeof(vis));
    priority_queue<Node> q;
    q.push((Node){0,1});
    while(!q.empty()){
        Node x=q.top();//快速找当前最短距离点
        q.pop();
        int u=x.u;
        if(vis[u]) continue;//如果该点已经为最优,则忽略
        vis[u]=true;
        for(int i=0;i<g[u].size();++i){//更新
            Edge &e=edge[g[u][i]];
            if(low[e.to]>low[u]+e.dis){
                low[e.to]=low[u]+e.dis;
                q.push((Node){low[e.to],e.to});
            }
        }
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    while(m--){
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        edge.push_back((Edge){a,b,c});
        g[a].push_back(edge.size()-1);
    }
    dijkstra();
    if(low[n]>=MAXN) printf("-1\n");
    else printf("%d\n",low[n]);
    return 0;
}
    原文作者:Dijkstra算法
    原文地址: https://blog.csdn.net/fangpinlei/article/details/42290327
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞