图的最短路径-----------Dijkstra算法详解(TjuOj2870_The Kth City)

做OJ需要用到搜索最短路径的题,于是整理了一下关于图的搜索算法:

图的搜索大致有三种比较常用的算法:

  • 迪杰斯特拉算法(Dijkstra算法)
  • 弗洛伊德算法(Floyd算法)
  • SPFA算法

Dijkstra算法使用了广度优先搜索解决赋权有向图或者无向图的单源最短路径问题,算法最终得到一个最短路径树。

算法的思路:

Dijkstra算法采用的是一种贪心的策略,声明一个数组dis来保存源点到各个顶点的最短距离和一个保存已经找到了最短路径的顶点的集合:T,初始时,原点 s 的路径权重被赋为 0 (dis[s] = 0)。若对于顶点 s 存在能直接到达的边(s,m),则把dis[m]设为w(s, m),同时把所有其他(s不能直接到达的)顶点的路径长度设为无穷大。初始时,集合T只有顶点s。
然后,从dis数组选择最小值,则该值就是源点s到该值对应的顶点的最短路径,并且把该点加入到T中,OK,此时完成一个顶点,
然后,我们需要看看新加入的顶点是否可以到达其他顶点并且看看通过该顶点到达其他点的路径长度是否比源点直接到达短,如果是,那么就替换这些顶点在dis中的值。
然后,又从dis中找出最小值,重复上述动作,直到T中包含了图的所有顶点。

举例:

下面我求下图,从顶点v1到其他各个顶点的最短路径

首先第一步,我们先声明一个dis数组,该数组初始化的值为:

我们的顶点集T的初始化为:T={v1}

既然是求 v1顶点到其余各个顶点的最短路程,那就先找一个离 1 号顶点最近的顶点。通过数组 dis 可知当前离v1顶点最近是 v3顶点。当选择了 2 号顶点后,dis[2](下标从0开始)的值就已经从“估计值”变为了“确定值”,即 v1顶点到 v3顶点的最短路程就是当前 dis[2]值。将V3加入到T中。
为什么呢?因为目前离 v1顶点最近的是 v3顶点,并且这个图所有的边都是正数,那么肯定不可能通过第三个顶点中转,使得 v1顶点到 v3顶点的路程进一步缩短了。因为 v1顶点到其它顶点的路程肯定没有 v1到 v3顶点短.

OK,既然确定了一个顶点的最短路径,下面我们就要根据这个新入的顶点V3会有出度,发现以v3 为弧尾的有: < v3,v4 >,那么我们看看路径:v1–v3–v4的长度是否比v1–v4短,其实这个已经是很明显的了,因为dis[3]代表的就是v1–v4的长度为无穷大,而v1–v3–v4的长度为:10+50=60,所以更新dis[3]的值,得到如下结果:

因此 dis[3]要更新为 60。这个过程有个专业术语叫做“松弛”。即 v1顶点到 v4顶点的路程即 dis[3],通过 < v3,v4> 这条边松弛成功。这便是 Dijkstra 算法的主要思想:通过“边”来松弛v1顶点到其余各个顶点的路程。

然后,我们又从除dis[2]和dis[0]外的其他值中寻找最小值,发现dis[4]的值最小,通过之前是解释的原理,可以知道v1到v5的最短距离就是dis[4]的值,然后,我们把v5加入到集合T中,然后,考虑v5的出度是否会影响我们的数组dis的值,v5有两条出度:< v5,v4>和 < v5,v6>,然后我们发现:v1–v5–v4的长度为:50,而dis[3]的值为60,所以我们要更新dis[3]的值.另外,v1-v5-v6的长度为:90,而dis[5]为100,所以我们需要更新dis[5]的值。更新后的dis数组如下图:

然后,继续从dis中选择未确定的顶点的值中选择一个最小的值,发现dis[3]的值是最小的,所以把v4加入到集合T中,此时集合T={v1,v3,v5,v4},然后,考虑v4的出度是否会影响我们的数组dis的值,v4有一条出度:< v4,v6>,然后我们发现:v1–v5–v4–v6的长度为:60,而dis[5]的值为90,所以我们要更新dis[5]的值,更新后的dis数组如下图:

然后,我们使用同样原理,分别确定了v6和v2的最短路径,最后dis的数组的值如下:

因此,从图中,我们可以发现v1-v2的值为:∞,代表没有路径从v1到达v2。所以我们得到的最后的结果为:

起点  终点    最短路径    长度
v1    v2     无          ∞
      v3     {v1,v3}    10
      v4     {v1,v5,v4}  50
      v5     {v1,v5}    30
      v6     {v1,v5,v4,v6} 60

转载自Ouyang_Lianjun的博客。

利用上述思路结题TjuOj 2870题目和代码如下:

Given a map of your country, there are N cities. The cities are labeled as 0, 1, ..., N - 1, and you live in city 0. Can you calculate out the K-th nearest city form you? If two or more cities have the same distance form you, you may assume that the city with smaller label is nearer than the city with bigger one.

Input

There are several cases. The first line of each case is two integers N and M (1 ≤ N ≤ 200, 0 ≤ M ≤ 10000), which is the number of cities in your country and the total number of roads in your country. There are three integers in each of the following M lines, ABC, which descript one road. A and B are the two cities that connected by that road, and C is the length of that road (1 ≤ C ≤ 2000). The roads are of both directions, and no two roads connect two same cities. There is at least one path between any two cities. At the last line of each case is a single integer K (1 ≤ K < N).

The last case is followed by a line with a single 0.

Output

Print the label of the K-th nearest city.

/*
 * 2870
 *   利用迪杰斯特拉克算法找单一源点出发的最短路径
 *  Created on: 2018年11月15日
 *      Author: Jeason
 */
#include <iostream>
#include <stdio.h>
#include <cstring>
using namespace std;
#define inf 999999999

int n, q;
int g[210][210];  //存放边的数组
int d[210];   //待求距离的数组
int vis[210];   //标记是否已访问

void Dijkstra(int k) {
    int i, j;
    for (i = 0; i < n; i++) {
        d[i] = inf;    //带求距离的数组
    }
    d[0] = 0;        //第一个点的距离到源点距离为0;
    memset(vis, 0, sizeof(vis));  //初始化访问标记数组(未被发现最短路径的点);
    int cnt = -1;
    int ans;
    for (i = 0; i < n; i++) {   //遍历N次,每次找到一个点到源点的最短路径;
        int Min = inf;    //最小值初始化正无穷
        int pos = -1;      //本次认为离远点最近的位置,初始化
        for (j = 0; j < n; j++) if (!vis[j]) {    //对每一个正在遍历的点,遍历相连的其他点(未被发现最短路径的点)
            if ( d[j] < Min ) {           //找当前最小的值
                Min = d[j];
                pos = j;

            }
        }
        cnt++;     //标记循环次数,每次找到一个稍微远一点的点
        if (cnt == k) {
            ans = pos;
            break;
        }
        vis[pos] = 1;
        for (j = 0; j < n; j++) {   //更新已经找到最近点后,通过该点其附近相连的点会不会比源点直接到该点近。
            if ( d[pos] + g[pos][j] < d[j]) {
                d[j] = g[pos][j] + d[pos];
            }
        }
    }
    cout << ans << endl;
}

int main() {
    int i, j;
    while (cin >> n) {   //读进来n个点
        if (n == 0) break;
        for (i = 0; i < n; i++) {    //把长度初始化最大
            for (j = 0; j < n; j++) {
                g[i][j] = inf;
            }
        }
        scanf("%d", &q);    //读入q条边
        for (i = 1; i <= q; i++) {
            int a, b, c;
            cin >> a >> b >> c;
            if (g[a][b] > c) {
                g[a][b] = g[b][a] = c;
            }
        }
        int k;
        scanf("%d", &k);
        Dijkstra(k);
    }
    return 0;
}

/*
Sample Input
4 3
0 1 120
0 2 180
1 3 40
3
4 3
0 1 120
0 3 60
3 2 30
1
0
Sample Output
2
3
*/

原文地址:https://www.cnblogs.com/JeasonIsCoding/p/9964963.html

时间: 2024-10-12 03:58:41

图的最短路径-----------Dijkstra算法详解(TjuOj2870_The Kth City)的相关文章

Dijkstra算法详解

前言 前几天研究的Bellman_Ford算法虽然可以算负权,可是时间复杂度高达O(NM),即使是采用了队列优化,也有可能被网格图卡回O(NM),所以今天我们就来研究一个新的,更快的,但同时只能在正权图上运行的算法:Dijkstra(朴素Dijkstra算法) Dijkstra基本思想及实现过程 我们首先需要以下几个数组:dist[],vis[],用邻接矩阵需要g[][],邻接表则需要v[],w[],head[],nxt[] 邻接表与邻接矩阵在此不做过多解释,不懂的同学请自行百度,dist[i]

最短路径问题---Dijkstra算法详解

原文地址:https://www.cnblogs.com/captain-dl/p/10254650.html

最短路径问题---Floyd算法详解

前言 Genius only means hard-working all one's life. Name:Willam Time:2017/3/8 1.最短路径问题介绍 问题解释: 从图中的某个顶点出发到达另外一个顶点的所经过的边的权重和最小的一条路径,称为最短路径 解决问题的算法: 迪杰斯特拉算法(Dijkstra算法) 弗洛伊德算法(Floyd算法) SPFA算法 之前已经对Dijkstra算法做了介绍(不懂的可以看这篇博客:Dijkstra算法详解),所以这篇博客打算对Floyd算法做

最短路算法 :Bellman-ford算法 &amp; Dijkstra算法 &amp; floyd算法 &amp; SPFA算法 详解

 本人QQ :2319411771   邮箱 : [email protected] 若您发现本文有什么错误,请联系我,我会及时改正的,谢谢您的合作! 本文为原创文章,转载请注明出处 本文链接   :http://www.cnblogs.com/Yan-C/p/3916281.html . 很早就想写一下最短路的总结了,但是一直懒,就没有写,这几天又在看最短路,岁没什么长进,但还是加深了点理解. 于是就想写一个大点的总结,要写一个全的. 在本文中因为邻接表在比赛中不如前向星好写,而且前向星效率并

最短路SPFA 算法详解

最短路SPFA 算法详解 适用范围:给定的图存在负权边,这时类似Dijkstra等算法便没有了用武之地,而Bellman-Ford算法的复杂度又过高,SPFA算法便派上用场了. 我们约定有向加权图G不存在负权回路,即最短路径一定存在.当然,我们可以在执行该算法前做一次拓扑排序,以判断是否存在负权回路,但这不是我们讨论的重点. 算法思想:我们用数组d记录每个结点的最短路径估计值,用邻接表来存储图G.我们采取的方法是动态逼近法:设立一个先进先出的队列用来保存待优化的结点,优化时每次取出队首结点u,并

数据结构:单源最短路径--Dijkstra算法

Dijkstra算法 单源最短路径 给定一带权图,图中每条边的权值是非负的,代表着两顶点之间的距离.指定图中的一顶点为源点,找出源点到其它顶点的最短路径和其长度的问题,即是单源最短路径问题. Dijkstra算法 求解单源最短路径问题的常用方法是Dijkstra(迪杰斯特拉)算法.该算法使用的是贪心策略:每次都找出剩余顶点中与源点距离最近的一个顶点. 算法思想 带权图G=<V,E>,令S为已确定了最短路径顶点的集合,则可用V-S表示剩余未确定最短路径顶点的集合.假设V0是源点,则初始 S={V

【转】AC算法详解

原文转自:http://blog.csdn.net/joylnwang/article/details/6793192 AC算法是Alfred V.Aho(<编译原理>(龙书)的作者),和Margaret J.Corasick于1974年提出(与KMP算法同年)的一个经典的多模式匹配算法,可以保证对于给定的长度为n的文本,和模式集合P{p1,p2,...pm},在O(n)时间复杂度内,找到文本中的所有目标模式,而与模式集合的规模m无关.正如KMP算法在单模式匹配方面的突出贡献一样,AC算法对于

支持向量机(SVM)(五)-- SMO算法详解

一.我们先回顾下SVM问题. A.线性可分问题 1.SVM基本原理: SVM使用一种非线性映射,把原训练            数据映射到较高的维.在新的维上,搜索最佳分离超平面,两个类的数据总可以被超平面分开. 2.问题的提出: 3.如何选取最优的划分直线f(x)呢? 4.求解:凸二次规划 建立拉格朗日函数: 求偏导数: B.线性不可分问题 1.核函数 如下图:横轴上端点a和b之间红色部分里的所有点定为正类,两边的黑色部分里的点定为负类. 设: g(x)转化为f(y)=<a,y> g(x)=

Tarjan算法详解

Tarjan算法详解 [概念] 在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly connected).如果有向图G的每两个顶点都强连通,称G是一个强连通图.非强连通图有向图的极大强连通子图,称为强连通分量(strongly connected components). 下图中,子图{1,2,3,4}为一个强连通分量,因为顶点1,2,3,4两两可达.{5},{6}也分别是两个强连通分量. [功能] Tarjan算法的用途之一是,求一个有向图G=(V,E)里极大强连