Dijkstra单元点最短路径(不含负权)

一、算法介绍

       Dijkstra(迪杰斯特拉)算法是典型的最短路径路由算法,用于计算一个节点到其他所有节点的最短路径。主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止。Dijkstra算法能得出最短路径的最优解,但由于它遍历计算的节点很多,所以效率低。
       与Bellman-ford不同的是,bellman-ford可处理含负权的图,Dijkstra不能处理含负权的图。除此之外Bellman-ford实质上是动态规划,而Dijkstra是贪心。且Dijkstra快于Bellman-ford。

二、算法详解

       为了求得最短路径,迪杰斯特拉提出了一种按最短路径长度递增的次序逐次生成最短路径的算法,其特点是首先求出最短的一条路径,然后再求长度次短的一条最短路径,以此类推求出其他最短路径。
       设集合U存放以求得的最短路径的端点,设源点为Vs,在初始状态下U={Vs},然后每求得一次最短路径,就将路径上的端点加入到U中,再以新加入的点为前哨更新最短路径记录,如此依次循环。直到U=V时为止。

  • 用数组dist[i]存储当前找到的从源点Vs到其他各顶点的最短路径的长度,其初态是如果存在边(Vs,Vi),则dist[i]=g.GetWeight(s,j),否则除非Vi=Vs,dist[s]=0,其他情况下dist[i]=net.GetInfinity(),这里net.GetInfinity()表示一个实际权值更大的一个数.
  • 设第一条最短路径使(Vs,Vj),也就是: dist[j]=max{dist[i]|Vi属于V-U}

  • 设刚求得的一条长度次短的最短路径的终点Vj,则此路径(Vs,…..Vj)的中间点都在U中,Vj满足:
    dist[j]=max{dist[i]|Vi属于V-U}

  • 将Vj加入到U中,下一条最短路径有两种形式:

  • (1)(Vs,……Vk)此路径的中间点不包括Vj,长度为dist[k]
    (2)(Vs,…,vj,vk)此路径Vk上一个点为Vj,整个路径长度为dist[j]+net.GetWeight(j,k)

通俗地讲,步骤为:

  • 初始时,S只包含源点,即S={v},v的距离为0。U包含除v外的其他顶点,即U={其余顶点},若v与U中顶点u有边,则(u,v)正常有权值,若u不是v的出边邻接点,则(u,v)权值为∞。
  • 从U中选取一个距离v最小的顶点k,把k,加入S中(该选定的距离就是v到k的最短路径长度)。
  • 以k为新考虑的中间点,修改U中各顶点的距离;若从源点v到顶点u的距离(经过顶点k)比原来距离(不经过顶点k)短,则修改顶点u的距离值,修改后的距离值的顶点k的距离加上边上的权。
  • 重复上两条步骤直到所有顶点都包含在S中。

三、例题

http://poj.org/problem?id=2253
       题目大意,有两只青蛙,分别在两个石头上,青蛙A想要到青蛙B那儿去,他可以直接跳到B的石头上,也可以跳到其他石头上,再从其他石头跳到B那儿,求青蛙从A到B的所有路径中最小的Frog Distance,我们定义Frog Distance为从A到B的一条路径中所跳的最大距离,例如,如果从A到B某条路径跳的距离是2,5,6,4,则Frog Distance就是6,题目输入的第一行代表石头的个数,当个数为0时结束程序,接着有n行,其中第2,3行分别代表A,B青蛙的座标,其他n-2行分别代表空的石头的座标,输出一个小数(保留三位),具体格式参见样例,注意每输出一个答案还要再空一行。

思路:
       在前人的引导下用Dijkstra解完这个题后发现其实更显而易见的是用Prim算法来做这道题
对于用Dijkstra来说,关键点在于理解:
定义 f(i,j)为i->j的路径上的最大跳的最小值,那么f(i,j)=min( f(i,j), max(f(i,k),f(k,j)) )。
对于Prim来说,关键点在于:
找到最小生成树之后查询path路径记录,由终点往回追溯到起点,并在过程中记录一个最大值即可。(因为最小生成树的原因可以保证此题找到的路径中的最大值小于其他路径中的最小值)

四、Talk is cheap,show me the code

#include <iostream>
#include <math.h>
using namespace std;

const int MAXSTONES = 200;

int input[MAXSTONES][2];
double map[MAXSTONES][MAXSTONES];

double dis[MAXSTONES];

double Dijkstra(int n);

int main(void)
{
    int n;
    int time = 1;
    while (cin >> n&&n != 0)
    {
        int i,j;
        for (i = 0; i < n; i++)
            cin >> input[i][0] >> input[i][1];
        for (i = 0; i < n; i++)
        {
            for (j = 0; j < i; j++)
            {
                if (j == i)
                    map[i][j] = 0;
                else
                    map[i][j] = map[j][i] = sqrt(pow(double(input[i][0] - input[j][0]), 2) + pow(double(input[i][1] - input[j][1]), 2));
            }
        }
        printf("Scenario #%d\nFrog Distance = %.3f\n\n", time++, Dijkstra(n));
        getchar();
    }
    return 0;
}
double Dijkstra(int n)
{
    bool*flag=new bool[n];
    int i, j;
    for (i = 0; i < n; i++)
    {
        dis[i] = map[0][i];
        flag[i] = false;
    }
    flag[0] = true;
    for (i = 0; i < n-1; i++)
    {
        double min = 1e60;
        int record=0;
        for (j = 0; j < n; j++)
        {
            if (dis[j] < min&&!flag[j])
            {
                record = j;
                min = dis[j];
            }
        }
        flag[record] = true;
        for (j = 0; j < n; j++)
        {
            if (!flag[j])
            {
                double max = dis[record]>map[record][j] ? dis[record] : map[record][j];
                if (dis[j]>max)
                    dis[j] = max;
            }
        }
    }
    return dis[1];
}
点赞