zoukankan      html  css  js  c++  java
  • Dijkstra算法

    Dijkstra算法

    单源最短路问题
    可以求出指定某个点到其他所有点的最短距离

    构建

    邻接矩阵的构造
    没有相连接的两个结点的距离时无穷大,本身到本身为0,其他正常
    1.先把所有的结点的距离初始化为无穷大
    2.本身与本身的距离设为0
    3.输入距离

    距离数组的构造
    构造一个一维距离数组来记录该点到其他点的最短距离
    1.进行赋值,从邻接矩阵中赋值
    2.本身到本身设为0

    复杂度分析

    O((o^{2}))
    优化后O((nlog_{n}))

    思路

    对每一个点进行遍历
    找个该点的相连点中,距离该点最近的点,然后标记最近点
    从标记的最近点进行遍历最近点相连接的点,进行距离更新

    因为距离用的是指定某个点的距离,所以最后求出来的距离数组是对于该点来说的
    相当于求出某个点a到另个点b的最短距离,然后这个距离和指定点到b的距离进行比大小,求距离短的那条路

    比如
    遍历为1时,表示结点为1的点进行查找,找到1的最近点,
    然后对该点的连接点进行查找,比较指定点到这个点的距离和指定点先到1再到这个点的距离哪个近,近的变成指定点到该点的距离

    最短路问题模板

    传送门

    输出最短距离

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<algorithm>
    #define INF 1000000
    using namespace std;
    const int maxn=1005;
    int edges[maxn][maxn];//邻接矩阵表示
    int n;//点的数
    int m;//边数
    int dis[maxn];//储存指定点到其他点的距离
    int vis[maxn];//标记顶点是否被访问过
    void init(){
        for(int i=0;i<=n;i++){
            for(int j=0;j<=n;j++){
                if(i==j)edges[i][j]=0;//到本身的距离
                else edges[i][j]=INF;//无穷大
            }
            vis[i]=0;
        }
        int a,b,d;
        for(int i=0;i<m;i++){
            scanf("%d%d%d",&a,&b,&d);
            if(edges[a][b]>d){
                edges[a][b]=edges[b][a]=d;
            }
        }
    }
    
    void Dij(int s){
        for(int i=1;i<=n;i++){
            dis[i]=edges[s][i];
        }
        vis[s]=1;//初始点标记
        int minn,mark;//进行查找,找到与当前点相连接的且距离最短的一个点,进行标记
        for(int i=1;i<=n;i++){
            minn=INF;
            for(int j=1;j<=n;j++){
                if(vis[j]==0&&dis[j]<minn){
                    minn=dis[j];
                    mark=j;
                }
            }
            vis[mark]=1;//标记该点
            //对这个标记点的相连点进行距离更新
            for(int k=1;k<=n;k++){//遍历所有点
                if(edges[mark][k]<INF){//如果说当前的点与前面标记的点相互连接
                    if(vis[k]==0&&dis[k]>dis[mark]+edges[mark][k]){//进行判断,看看是否可以更新为更小的的距离
                        //dis[k]是指定点到该点的距离,dis[mark]是指定点标记点的距离,edges[mark][k]是标记点到该点的距离
                        dis[k]=dis[mark]+edges[mark][k];
                    }
                }
            }
        }
    }
    
    int main(){
        while(scanf("%d%d",&n,&m)==2){
    	    if(n==0&&m==0)break;
            init();//初始化并且创建;邻接矩阵
            int s,t;
            scanf("%d%d",&s,&t);
            Dij(s);
            if(dis[t]==INF){
                printf("-1
    ");
            }else{
                printf("%d
    ",dis[t]);
            }
        }
        return 0;
    }
    

    输出路径

    加个path[]一维数组,且在dij算法里两处不断更新
    然后用while语句访问,while(path[]!=-1)不断变化下标,但是这样只能倒着输出
    可以再创建一个数组或者是栈,使得正序输出

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<algorithm>
    #include<vector>
    #define INF 1000000
    using namespace std;
    const int maxn=1005;
    int edges[maxn][maxn];//邻接矩阵表示
    int n;//点的数
    int m;//边数
    int dis[maxn];//储存指定点到其他点的距离
    int vis[maxn];//标记顶点是否被访问过
    int path[maxn];
    void init(){
        for(int i=0;i<=n;i++){
            for(int j=0;j<=n;j++){
                if(i==j)edges[i][j]=0;//到本身的距离
                else edges[i][j]=INF;//无穷大
            }
            vis[i]=0;
        }
        int a,b,d;
        for(int i=0;i<m;i++){
            scanf("%d%d%d",&a,&b,&d);
            if(edges[a][b]>d){
                edges[a][b]=edges[b][a]=d;
            }
        }
    }
    
    void Dij(int s){
        for(int i=1;i<=n;i++){
            dis[i]=edges[s][i];
            path[i]=-1;
        }
        vis[s]=1;//初始点标记
        int minn,mark;
        for(int i=1;i<=n;i++){
            minn=INF;
            for(int j=1;j<=n;j++){
                if(vis[j]==0&&dis[j]<minn){
                    minn=dis[j];
                    mark=j;
                    path[mark]=i;//不断更新
                }
            }
            vis[mark]=1;//标记该点
            for(int k=1;k<=n;k++){
                if(edges[mark][k]<INF){
                    if(vis[k]==0&&dis[k]>dis[mark]+edges[mark][k]){
                        dis[k]=dis[mark]+edges[mark][k];
                        path[k]=mark;//不断地更新mark后面地那个点
                    }
                }
            }
        }
    }
    
    int main(){
        while(scanf("%d%d",&n,&m)==2&&n&&m){
            init();
            int s,t;
            scanf("%d%d",&s,&t);
            Dij(s);
            for(int i=1;i<=n;i++)printf("%d ", path[i]);//-1表示不走,path[]的值是点的前一个位置的下标
        }
        return 0;
    }
    
    

    双重权值

    传送门

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<algorithm>
    #define INF 0x3f3f3f3f
    using namespace std;
    const int maxn=1005;
    int edges[maxn][maxn],edgec[maxn][maxn];//邻接矩阵表示
    int n,m;//边和点
    int dis[maxn],cost[maxn];//储存指定点到其他点的距离
    bool vis[maxn];//标记顶点是否被访问过
    void init(){
        for(int i=0;i<=n;i++){
            for(int j=0;j<=n;j++){
                if(i==j)edges[i][j]=edgec[i][j]=0;//到本身的距离
                else edges[i][j]=edgec[i][j]=INF;//无穷大
            }
            vis[i]=false;
        }
    }
    void Dij(int s){
        for(int i=1;i<=n;i++){
            dis[i]=edges[s][i];
            cost[i]=edgec[s][i];
        }
        int minn,mark;
        for(int i=1;i<=n;i++){
            minn=INF;
            for(int j=1;j<=n;j++){
                if(vis[j]==0&&dis[j]<minn){
                    minn=dis[j];
                    mark=j;
                }
            }
            vis[mark]=true;
            for(int k=1;k<=n;k++){
                if(edges[mark][k]<INF){
                    if(!vis[k]&&dis[k]>dis[mark]+edges[mark][k]){
                        dis[k]=dis[mark]+edges[mark][k];
                        cost[k]=cost[mark]+edgec[mark][k];
                    }else if(!vis[k]&&dis[k]==dis[mark]+edges[mark][k]){
                        if(cost[k]>cost[mark]+edgec[mark][k]){
                            cost[k]=cost[mark]+edgec[mark][k];
                        }
                    }
                }
            }
        }
    }
    
    int main(){
        while(~scanf("%d%d",&n,&m)){
            if(n==m&&n==0)break;
            init();//初始化并且创建;邻接矩阵
            int a,b,d,p;
            for(int i=0;i<m;i++){
                scanf("%d%d%d%d",&a,&b,&d,&p);
                if(edges[a][b]>d){
                    edges[a][b]=edges[b][a]=d;
                    edgec[a][b]=edgec[b][a]=p;
                }
            }
            int s,t;
            scanf("%d%d",&s,&t);
            Dij(s);
            printf("%d %d
    ",dis[t],cost[t]);
        }
        return 0;
    }
    
    

    优化

    • 优先队列
    • 前向星
    • 邻接表代替邻接矩阵

    链式前向星+优先队列优化(边少的情况)

    /*
    输出s到t的最短路
    输入n,m
    以及m个u,v,w
    输出最短路
    */
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <queue>
    #define INF 0x3f3f3f3f
    using namespace std;
    const int maxn=1e5+5,maxm=2e5+5;
    int head[maxm],vis[maxn],dis[maxn];//dis[]数组表示到制定点的最短距离,vis[]数组表示某个点是否走过
    int n,m,s,t;
    int cnt=1;//记录图的结构体的下标
    int pre[maxn];//存路径用
    struct edge{
        int to;//到哪
        int from;//从哪开始
        int w;
    }e[maxm];
    inline void add(int u,int v,int w){//链式前向星存图
        e[cnt].to=v;
        e[cnt].w=w;
        e[cnt].from=head[u];
        head[u]=cnt;
        cnt++;
    }
    struct node{
        int dis;//dis表示到起始点的距离
        int pos;//pos表示该点的下标
        bool operator < (const node &x)const{//符号重载,对距离排序,针对于在优先队列里
            return x.dis<dis;
        }
    };
    std::priority_queue<node> q;//构造优先队列
    void dijkstra(){
        q.push((node){dis[s],s});//把初始位置和距离放进优先队列
        while(!q.empty()){
            node tmp=q.top();
            q.pop();
            int u=tmp.pos;
            if(vis[u])continue;//如果这个点走过了
            vis[u]=1;//标记走过
            for(int i=head[u];i;i=e[i].from){
                int v=e[i].to;
                if(!vis[v]&&dis[v]>dis[u]+e[i].w){
                    dis[v]=dis[u]+e[i].w;
                    pre[v]=u;
                    q.push((node){dis[v],v});
                  }
            }
        }
    }
    int main(){
        scanf("%d%d",&n,&m);
        s=1,t=n;//s到t
        for(int i=1;i<=n;i++)dis[i]=INF;//初始化
        dis[s]=0;
        for(int i=1;i<=m;i++){
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            add(u,v,w);
            add(v,u,w);//构成无向图
        }
        dijkstra();
        printf("%d
    ",dis[t]);//输出1到n的最短路
    }
    

    邻接表+优先队列(点少的情况)

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <queue>
    #define INF 0x3f3f3f3f
    using namespace std;
    const int maxn=1e5+5;
    struct edge{
        int to,cost;
    };
    int vis[maxn];//表示该点是否走过
    int n,m,s,t;//n个点,m个边,s起点,t终点
    int dis[maxn];//最短距离
    struct HeadNode{
        int d,u;//d为距离,u为结点位置
        bool operator < (const HeadNode& rhs) const{//重载HeadNode结构体的<运算符,如果放在优先队列里,将会按照距离排序
            return d>rhs.d;
        }
    };
    vector<edge> g[maxn];
    void Dij(){
        for(int i=1;i<=n;i++){
            dis[i]=INF;
        }
        dis[s]=0;
    
        priority_queue<HeadNode>q;
        q.push((HeadNode){0,s});//把类型为HeadNode,值为0,s
        while(!q.empty()){
            HeadNode x = q.top();//x是队列顶
            q.pop();//把顶取出,顶就是距离最短的那个点
            int u=x.u;
            if(vis[u])continue;
            vis[u]=1;
            for(int i=0;i<g[u].size();i++){//遍历与距离最短点相连接的点
                edge e=g[u][i];
                if(dis[e.to]>dis[u]+e.cost){//如果直接到该点的距离大于从通过u点再到该点的距离的话
                    dis[e.to]=dis[u]+e.cost;//松弛
                    q.push((HeadNode){dis[e.to],e.to});
                }
            }
        }
    
    }
    
    int main(){
        cin>>n>>m>>s>>t;
        for(int i=1;i<=m;i++){
            edge e;
            int x;
            scanf("%d%d%d",&x,&e.to,&e.cost);//x出发到e.to,花费e.cost
            g[x].push_back(e);//只有这个的话就是有向图
    
            g[e.to].push_back((edge){x,e.cost});//构建无向图
        }
        Dij();
        if(dis[t]==INF)printf("-1
    ");//不存在最短路
        else printf("%d
    ",dis[t]);
        return 0;
    }
    

    输出路径模板

    增加pre[数组],pre[s]=0,反向输出路径

    
    #include<bits/stdc++.h>
    using namespace std;
    const int inf=1000000002;
    const int maxn=100100,maxm=200200;
    int head[maxm],vis[maxn],dis[maxn];//dis[]数组表示到制定点的最短距离,vis[]数组表示某个点是否走过
    int i,j;
    int u,v,w,z;
    int n,m,s,t;
    int cnt=1;//记录图的结构体的下标
    int pre[maxn];
    struct edge{
        int to;//到哪
        int from;//从哪开始
        int w;
    }e[maxm];
    inline void add(int u,int v,int w){
        e[cnt].to=v;
        e[cnt].w=w;
        e[cnt].from=head[u];
        head[u]=cnt;
        cnt++;
    }
    struct node
    {
        int dis;//dis表示到起始点的距离
        int pos;//pos表示该点的下标
        bool operator < (const node &x)const{//符号重载,对距离排序,针对于在优先队列里
            return x.dis<dis;
        }
    };
    std::priority_queue<node> q;//构造优先队列
    inline void dijkstra()
    {
        q.push((node){dis[s],s});//把初始位置和距离放进优先队列
        while(!q.empty()){
            node tmp=q.top();
            q.pop();
            int u=tmp.pos;
            if(vis[u])continue;//如果这个点走过了
            vis[u]=1;//标记走过
            for(i=head[u];i;i=e[i].from){
                int v=e[i].to;
                if(!vis[v]&&dis[v]>dis[u]+e[i].w){
                    dis[v]=dis[u]+e[i].w;
                    pre[v]=u;
                    q.push((node){dis[v],v});
                  }
            }
    
        }
    }
    int main(){
        scanf("%d%d",&n,&m);
        s=1;//起始点
        pre[s]=0;
        for(i=1;i<=n;i++)dis[i]=inf;//初始化
        dis[s]=0;
    
        for(i=1;i<=m;i++){
            scanf("%d%d%d",&u,&v,&w);
            add(u,v,w);
            add(v,u,w);
        }
        dijkstra();
        printf("%d
    ",dis[n]);
    
        while(pre[n]!=0){
            printf("%d
    ",pre[n]);
            n=pre[n];
        }
    }
    
    
  • 相关阅读:
    UIimageView和UIimage的小区别
    transform
    block的一些注意事项
    category与协议的不同
    类扩展和category的小区别
    category的概念
    OC中的类扩展
    Java学习 第三章 java基础(三)
    Java学习 第三章 java基础(二)
    Java学习 第三章 java基础(一)
  • 原文地址:https://www.cnblogs.com/Emcikem/p/11333851.html
Copyright © 2011-2022 走看看