关闭

最短路——dijkstra

标签: 最短路dijkstra
497人阅读 评论(2) 收藏 举报
分类:

                                                                          


                                最短路——dijkstra

     


          这次写一下我用dijkstra算法做单源最短路的经历。

 

          首先先引入一个问题:给你两个数 m,n。n代表一共有n个村庄。之后有m行,每行包含三个数 from,to,dist.这三个数代表着,从村庄from到村庄to有dist 这么长的距离,此为无向图。

          现在想知道从村庄1到村庄n的最短的路径是多少。


          相关题目 here   http://poj.org/problem?id=2387 


          首先写的是很朴素的dijkstra算法:用邻接矩阵(二维数组)存图,外层for 1->n; 然后先找出dist[] 数组里面最小的一个数的编号(代表从目的地到这个编号的村庄的最短距离)。这个编号里的值此时已经可以确定是从出发点到这个这个编号的最短路径。然后在用一个for-1>n;通过这个最短距离,来改变其他村庄到出发点的距离。


          这便是dijkstra算法的核心思想,后面的优化只是优化的数据结构,对思想并未优化。

            

           初始有一个dist[]数组存放从起点到任一点的初始距离。


          举个简单的例子,已经确定从村庄1到村庄2的最短距离是100,已知从村庄2到村庄3的距离是100,从村庄1到村庄3的距离是500;

那么就可以用从 1->2->3(200) 来改变从 1->3(500)  的距离,此时被称为松弛边。但是如果1->2>3 大于 1->3的距离时,这个边就不必松弛。

        

          直接看代码吧:

          

               

<strong>#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<queue>
using namespace std;
const int inf=0x3f3f3f3f; //最大值,即1061109567
int main()
{
    int m,n;
    while(~scanf("%d%d",&m,&n))
    {
        int fro,to,d;
        int a[n+2][n+2]; //邻接矩阵存图
        int dist[n+2],vist[n+2];
        memset(a,inf,sizeof(a));  //inf可用memset
        memset(vist,0,sizeof(vist));
        memset(dist,inf,sizeof(dist));
        dist[1]=0;
        for(int i=0; i<m; i++)
        {
            scanf("%d%d%d",&fro,&to,&d);
            a[fro][to]=a[to][fro]=min(d,a[to][fro]);
        }
        for(int i=1; i<=n; i++)
        {
            int max1=inf;
            int x;
            //找到最短的一条路径来松弛其他的边
            for(int y=1; y<=n; y++)
                if(!vist[y]&&dist[y]<=max1)
                    max1=dist[x=y];
            vist[x]=1;//标记已经找到了从出发点到编号x的最短路,下次不再访问·
            for(int y=1; y<=n; y++)
            {//松弛边操作
                if(!vist[y]&&dist[y]>dist[x]+a[x][y])
                    dist[y]=dist[x]+a[x][y];
            }
        }
        printf("%d\n",dist[n]);
    }
}</strong>


              然后看到书上说可以用优先队列,来找到最短的那一条路。优先队列:即一个队列,在入队的同时其内部进行自动排序操作,可指定优先级,此处以小值优先的方式找到dist[]数组中的最小值。


         

#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<queue>
using namespace std;
const int inf=0x3f3f3f3f;
struct node
{
    //dis指dist[]数组中的距离,num指村庄编号
    int dis,num;
    //构造函数,便于赋初值
    node(int dis1,int num1):dis(dis1),num(num1) {}
    //指定优先级,以小值优先
    bool operator < (const node &a) const
    {
        return dis>a.dis;
    }
};
int a[1003][1006];
int vist[100000];
int d[100000];
int main()
{
    int n,m;

    while(~scanf("%d%d",&m,&n))
    {
        int fro,to,w;
        priority_queue<node> q;

        memset(a,inf,sizeof(a));
        memset(vist,0,sizeof(vist));
        memset(d,inf,sizeof(d));
        d[1]=0;
        for(int i=0; i<m; i++)
        {
            scanf("%d%d%d",&fro,&to,&w);
            //去重边
            a[fro][to]=a[to][fro]=min(w,a[to][fro]);
        }
        //将dist[1]的内容用node结构体存储并压进优先队列
        q.push(node(0,1));
        
        while(!q.empty())
        {
           node tmp=q.top();
           q.pop();
           //int u代指第一个的代码中的x,即有最小路径的那个编号
            int u=tmp.num;
           if(vist[u]) continue;
           vist[u]=1;
           for(int i=1;i<=n;i++)
           {
               if(d[i]>d[u]+a[i][u])
               {
                   d[i]=d[u]+a[i][u];
                   q.push(node(d[i],i));
               }
           }
        }
        printf("%d\n",d[n]);
    }
}


          但是上面的也有缺陷,那就是存储的图太小了,最多只能存储一千多个村庄之间的相互关系,如a[1005][1005];而如果遇到稀疏图的话,就太浪费空间与时间了。于是下面再写一个用vector数组模拟邻接表来存储图的dijkstra算法。

           vector:可动态分配空间,被称为动态数组;定义它vector<int> G.有三种主要操作方式:1):G.clear();//清空   2)G.size();//表长    3)G.push_back();//向表尾添加元素



         下面给出代码:


         

#include <iostream>
#include <vector>
#include <cstring>
#include<stdio.h>
#include<queue>
#include <algorithm>
using namespace std;
const int inf=0x3f3f3f3f;
const int MAX=10000;
struct edge  //存边
{
    int to,dist;
    //构造函数,可以赋初值
    edge(int to1=0,int dist1=0):to(to1),dist(dist1) {}
};
struct node  //入队列
{
    int num,dist;
    node(int num1=0,int dist1=0):num(num1),dist(dist1) {}
    bool operator < (const node &a) const
    {
        return dist>a.dist;
    }
};
vector<edge> G[MAX];
inline void addedge(int fro,int to, int dis)
{
    //无向图,有向图则舍弃第二语句
    G[fro].push_back(edge(to,dis));
    G[to].push_back(edge(fro,dis));
}
int m,n;

int vist[MAX],dist[MAX];

void dijkstra(int start)
{
    for(int i=1; i<=n; i++)
    {
        dist[i]=inf;
        vist[i]=0;
    }
    dist[start]=0;
    priority_queue<node>q;
    q.push(node(start,0));
    node tmp;
    while(!q.empty())
    {
        tmp=q.top();
        q.pop();
         //u代指第一个的代码中的x,即有最小路径的那个编号
        int u=tmp.num;

        //标记已经找到了从出发点到编号x的最短路,下次不再访问·
        if(vist[u]) continue;
        vist[u]=1;
        //村庄u的最短路已经找到,下面来松弛能通过u村庄来优化的边
        for(int i=0; i<G[u].size(); i++)
        {
            //用ff来减少代码复杂度
            edge &ff=G[u][i];

            if(dist[ff.to]>dist[u]+ff.dist)
            {
                dist[ff.to]=dist[u]+ff.dist;
                q.push(node(ff.to,dist[ff.to]));
            }
        }
    }
}

int A[MAX],B[MAX],C[MAX];
int main()
{
    while(scanf("%d%d",&m,&n)!=EOF)
    {
        for(int i=0;i<=n;i++)
            G[i].clear();  //必须要使用清空函数,因为有循环且是全局变量。
        for(int i=0; i<m; i++)
            scanf("%d%d%d",&A[i],&B[i],&C[i]);
            //存入邻接表中
        for(int i=0; i<m; i++)
            addedge(A[i],B[i],C[i]);

        //起点是一
        dijkstra(1);

        printf("%d\n",dist[n]);
    }
}

1
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:13622次
    • 积分:556
    • 等级:
    • 排名:千里之外
    • 原创:43篇
    • 转载:0篇
    • 译文:0篇
    • 评论:2条
    最新评论