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;
}

时间: 2024-10-13 12:31:09

eoj1817 dijkstra单元最短路径 普通方法+二叉堆优化的相关文章

EOJ 1848 你是ACM吗? 用二叉堆优化dijkstra + spfa算法的学习

Description  随着中国经济的腾飞,中国的物流产业迎来了发展的春天.特别是在上海这样一个拥有广阔国内腹地的国际化大都市,物流业以空前的速度膨胀. 当然是大蛋糕就会吸引许多馋嘴猫,馋嘴猫多了就会有残酷的竞争.当大量资金流入物流产业时,KOP 集团为了稳坐在国内物流业的第一把交椅,决定对现行的运输方案进行改良,以减少自己的成本同时使其它竞争者知难而退. 作为世界100强的KOP集团当然知道要找到最优运输方案,肯定得靠数学和算法很好的软件工程师,于是他们理所当然地找到华东师范大学软件学院.决

PHP利用二叉堆实现TopK-算法的方法详解

前言 在以往工作或者面试的时候常会碰到一个问题,如何实现海量TopN,就是在一个非常大的结果集里面快速找到最大的前10或前100个数,同时要保证 内存和速度的效率,我们可能第一个想法就是利用排序,然后截取前10或前100,而排序对于量不是特别大的时候没有任何问题,但只要量特别大是根本不可能 完成这个任务的,比如在一个数组或者文本文件里有几亿个数,这样是根本无法全部读入内存的,所以利用排序解决这个问题并不是最好的,所以我们这里就用 php去实现一个小顶堆来解决这个问题. 二叉堆 二叉堆是一种特殊的

D&amp;F学数据结构系列——二叉堆

二叉堆(binary heap) 二叉堆数据结构是一种数组对象,它可以被视为一棵完全二叉树.同二叉查找树一样,堆也有两个性质,即结构性和堆序性.对于数组中任意位置i上的元素,其左儿子在位置2i上,右儿子在左儿子后的单元2i+1中,它的父亲在[i/2](向下取整)中. 因此,一个数据结构将由一个数组.一个代表最大值的整数.以及当前的堆的大小组成.一个典型的优先队列(priority queue)如下: 1 #ifndef _BinHeap_H 2 struct HeapStruct; 3 type

算法—二叉堆

实现栈或是队列与实现优先队列的最大不同在于对性能的要求.对于栈和队列,我们的实现能够在常数时间内完成所有操作:而对于优先队列,插入元素和删除最大元素这两个操作之一在最坏情况下需要线性时间来完成.我们接下来要讨论的基于数据结构堆的实现能够保证这两种操作都能更快地执行. 1.堆的定义 数据结构二叉堆能够很好地实现优先队列的基本操作.在二叉堆的数组中,每个元素都要保证大于等于另两个特定位置的元素.相应地,这些位置的元素又至少要大于等于数组中的另两个元素,以此类推.如果我们将所有元素画成一棵二叉树,将每

堆排序:什么是堆?什么是最大堆?二叉堆是什么?堆排序算法是怎么样的?PHP如何实现堆排序?

本文标签:  堆排序 php php算法 堆排序算法 二叉堆 数据结构 REST   服务器 什么是堆 这里的堆(二叉堆),指得不是堆栈的那个堆,而是一种数据结构. 堆可以视为一棵完全的二叉树,完全二叉树的一个"优秀"的性质是,除了最底层之外,每一层都是满的,这使得堆可以利用数组来表示,每一个结点对应数组中的一个元素. 数组与堆之间的关系 二叉堆一般分为两种:最大堆和最小堆. 什么是最大堆 堆中每个父节点的元素值都大于等于其孩子结点(如果存在),这样的堆就是一个最大堆 因此,最大堆中的

在A*寻路中使用二叉堆

接上篇:A*寻路初探 GameDev.net 在A*寻路中使用二叉堆 作者:Patrick Lester(2003年4月11日更新) 译者:Panic 2005年3月28日 译者序:     这一篇文章,是"A* Pathfinding for Beginners.",也就是我翻译的另一篇文章<A*寻路初探>的补充,在这篇文章里,作者再一次展现了他阐述复杂话题的非凡能力,用通俗易懂的语句清晰的解释了容易让人迷惑的问题.还是那句话,如果你看了这篇文章仍然无法领会作者的意图,那

二叉堆部分练习

本练习主要做了几个工作: 1.给定一个数组来初始化二叉堆,第一种方法是通过不断插入,时间复杂度是O(nlgn),第二种方法是先把数组填入二叉堆,再从下标为H->SIZE/2的节点开始下滤,这是因为只有下标小于为H->SIZE/2才有孩子,从而可以用线性时间完成二叉堆的初始化. 2.二叉堆的下滤和上滤,对二叉堆关键字的操作诸如删除最小,增加某关键字值,减少某关键字值等会可能改变堆性质的操作,必须用上滤和下滤来保证二叉堆的性质,比如增加了某关键字的值,该关键字可能往下走,因此对该节点采用下滤,与之

堆之二叉堆

堆的定义 堆通常是一个可以被看做一棵树,它满足下列性质: 堆中任意节点的值总是不大于(不小于)其子节点的值: 堆总是一棵完全树. 将任意节点不大于其子节点的堆叫做最小堆或小根堆,而将任意节点不小于其子节点的堆叫做最大堆或大根堆.常见的堆有二叉堆.左倾堆.斜堆.二项堆.斐波那契堆等等. 二叉堆 堆有两种性质:结构性和堆序性 结构性:堆是一颗完全二叉树.若设完全二叉树的高度是h,则它的节点数是2^h到2^(h+1) - 1:则节点数为N的完全二叉树的高度O(logn). 完全二叉树中父子节点位置关系

POJ 2010 - Moo University - Financial Aid 初探数据结构 二叉堆

考虑到数据结构短板严重,从计算几何换换口味= = 二叉堆 简介 堆总保持每个节点小于(大于)父亲节点.这样的堆被称作大根堆(小根堆). 顾名思义,大根堆的数根是堆内的最大元素. 堆的意义在于能快速O(1)找到最大/最小值,并能持续维护. 复杂度 push() = O(logn); pop() = O(logn); BinaryHeap() = O(nlogn); 实现 数组下标从1开始的情况下,有 Parent(i) = i >> 1 LChild(i) = i << 1 RChi