一、知识概述

今天我们要复习的内容是图论中的最短路算法,我们在这里讲3种最短路求法,分别是:floyd,dijkstra,spfa。

那么我们从几道例题来切入今天讲解的算法。

二、典型例题

1、热浪

题目描述

德克萨斯纯朴的民眾们这个夏天正在遭受巨大的热浪!!!他们的德克萨斯长角牛吃起来不错,可是他们并不是很擅长生產富含奶油的乳製品。Farmer John此时以先天下之忧而忧,后天下之乐而乐的精神,身先士卒地承担起向德克萨斯运送大量的营养冰凉的牛奶的重任,以减轻德克萨斯人忍受酷暑的痛苦。

FJ已经研究过可以把牛奶从威斯康星运送到德克萨斯州的路线。这些路线包括起始点和终点先一共经过T (1 <= T <= 2,500)个城镇,方便地标号為1到T。除了起点和终点外地每个城镇由两条双向道路连向至少两个其它地城镇。每条道路有一个通过费用(包括油费,过路费等等)。

给定一个地图,包含C (1 <= C <= 6,200)条直接连接2个城镇的道路。每条道路由道路的起点Rs,终点Re (1 <= Rs <= T; 1 <= Re <= T),和花费(1 <= Ci <= 1,000)组成。求从起始的城镇Ts (1 <= Ts <= T)到终点的城镇Te(1 <= Te <= T)最小的总费用。

输入输出格式

输入格式:

第一行: 4个由空格隔开的整数: T, C, Ts, Te

第2到第C+1行: 第i+1行描述第i条道路。有3个由空格隔开的整数: Rs, Re和Ci

输出格式:

一个单独的整数表示从Ts到Te的最小总费用。数据保证至少存在一条道路。

输入输出样例

输入样例#1:

7 11 5 4
2 4 2
1 4 3
7 2 2
3 4 3
5 7 5
7 3 3
6 1 1
6 3 4
2 4 3
5 6 3
7 2 1

输出样例#1:

7

说明

【样例说明】

5->6->1->4 (3 + 1 + 3)

2、城市路

时间限制: 1 Sec 内存限制: 128 MB

题目描述

罗老师被邀请参加一个舞会,是在城市n,而罗老师当前所处的城市为1,附近还有很多城市2~n-1,有些城市之间没有直接相连的路,有些城市之间有直接相连的路,这些路都是双向的,当然也可能有多条。

现在给出直接相邻城市的路长度,罗老师想知道从城市1到城市n,最短多少距离。

输入

输入n, m,表示n个城市和m条路

接下来m行,每行a b c, 表示城市a与城市b有长度为c的路

输出

输出1到n的最短路

如果1到达不了n,就输出-1

样例输入

5 5
1 2 20
2 3 30
3 4 20
4 5 20
1 5 100

样例输出

90

提示

【数据规模和约定】

1<=n<=2000

1<=m<=10000

0<=c<=10000

三、算法分析

(一)dijkstra算法(对于例题1)

1、算法实现

dijkstra有朴素算法与堆优化的两种。

这个朴素算法非常好写(垃圾的我调了2个小时),我就不多讲了。

我们可以用优先队列来替代堆优化dijkstra,优化扫最小值的过程。

具体实现可以看代码

2、时间复杂度

朴素算法时间复杂度为\(O(N^2)\)

堆优化时间复杂度\(O((n+m)logm)\)

3、代码实现

(1) 朴素算法

//O(n2)的朴素dijkstra
#include<bits/stdc++.h>
using namespace std;
int n,m,tx,ty,dis[5005],a[2505][2505];
bool b[5005];
int main()
{
scanf("%d%d%d%d",&n,&m,&tx,&ty);
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
a[i][j]=100000000;
for (int i=1;i<=m;i++)
{
int x,y,z=0;
scanf("%d%d%d",&x,&y,&z);
a[x][y]=min(a[x][y],z);
a[y][x]=min(a[y][x],z);
}
for (int i=1;i<=n;i++)
{
dis[i]=a[tx][i];
}
dis[tx]=0;
b[tx]=true;
for (int i=1;i<=n-1;i++)
{
int minn=100000000;
int k=0;
for (int j=1;j<=n;j++)
{
if ((!b[j])&&(dis[j]<minn))
{
minn=dis[j];
k=j;
}
}
if (k==0) break;
b[k]=true;
for (int z=1;z<=n;z++)//这里不能再用j,我也不知道为什么。这个地方错了2个小时。。。
{
if(a[z][k]<100000000)
if (dis[k]+a[k][z]<dis[z])
dis[z]=dis[k]+a[k][z];
}
}
printf("%d",dis[ty]);
}

(2)堆优化代码

#include<bits/stdc++.h>
using namespace std;
#define INF 2147483647
int n,m,s,tot;
int Next[500005],head[500005],val[500005],to[500005];
int dis[200005],vis[200005],cnt[200005];
void add(int x,int y,int z)
{
tot++;
to[tot]=y;
val[tot]=z;
Next[tot]=head[x];
head[x]=tot;
}
void dijkstra()
{
priority_queue<pair<int,int> > q;
for (int i=1;i<=n;i++)
dis[i]=INF;
for (int i=1;i<=n;i++)
vis[i]=0;
dis[s]=0;
q.push(make_pair(0,s));
while (q.size())
{
int x=q.top().second; q.pop();
if (vis[x]) continue;
vis[x]=1;
for (int i=head[x];i;i=Next[i])
{
int y=to[i]; int z=val[i];
if (dis[y]>dis[x]+z)
{
dis[y]=dis[x]+z;
q.push(make_pair(-dis[y],y));
}
}
}
}
int main()
{
scanf("%d%d%d",&n,&m,&s);
for (int i=1;i<=m;i++)
{
int x,y,z=0;
scanf("%d%d%d",&x,&y,&z);
add(x,y,z);
}
dijkstra();
for (int i=1;i<=n;i++)
printf("%d ",dis[i]);
return 0;
}

(二)spfa算法(对于例题2)

spfa算法作为重点且最常用。

在稀疏图或有负边权的图上,Dijkstra就失去用武之地了。此时需要使用spfa算法。

1、算法分析

spfa算法与dijkstra算法其实很相像,都需要用到松弛操作。而spfa算法使用一个队列,第一步从源点s开始,把s放进队列。对队列中的每个元素x,广度优先遍历其出边,并针对该元素x的这条出边进行松弛操作。如果松弛可以进行,则把这条出边可达的节点加入队列中。

注意:存在负权环的图中不存在最短路径,SPFA可以用来判断图中是否存在负权环路。

2、时间复杂度

玄学。\(O(kE)\)。对于随机数据来说复杂度较低。

用邻接矩阵存储会导致spfa算法性能大大降低,故需要使用边表(前向星)存图。

3、代码实现

#include<bits/stdc++.h>
using namespace std;
#define INF 2147483647
int n,m,s,tot;
int Next[500005],head[500005],val[500005],to[500005];
//注意,使用next会语法错误,队列要开双倍内存
int dis[50005],vis[50005],cnt[50005];
void add(int x,int y,int z)
{
tot++;
to[tot]=y;
val[tot]=z;
Next[tot]=head[x];
head[x]=tot;
}
bool spfa()
{
queue<int> q;//c++队列的基本操作
for (int i=1;i<=n;i++)
dis[i]=INF;
memset(vis,false,sizeof(vis));
dis[s]=0; vis[s]=true;
q.push(s);//入队列
while (!q.empty())
{
int u=q.front(); q.pop();
vis[u]=false;//队首出队,并且队首可再次访问
for (int i=head[u];i;i=Next[i])
{
int v=to[i];
if (dis[v]>dis[u]+val[i])
{
dis[v]=dis[u]+val[i];
if (!vis[v])
{
q.push(v);
vis[v]=true;
cnt[v]++;
if (cnt[v]>n) return false;
//一个点如果被松弛大于n次,证明不能到达终点。
}
}
}
}
return true;
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++)
{
int x,y,z=0;
scanf("%d%d%d",&x,&y,&z);
add(x,y,z);
add(y,x,z);
//这里我没有考虑有重边的情况
}
s=1;//为了使大家能更好的灵活应用,所以令起点为1
if (spfa())
{
printf("%d",dis[n]);
} else printf("-1");
}

(三)Floyd

Floyd是一个多元最短路径算法。也就是说,Floyd算法可以求出图中任意两点间的距离。由于Floyd编写较为简单,故不放例题。

以下为floyd的核心代码

for (int k=1;k<=n;k++)
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
if (dis[i][k]+dis[k][j]<dis[i][j])
dis[i][j]=dis[i][k]+dis[k][j];

扩展算法应用(差分约束系统)

算法简介

四、课后作业

1、NOIP2016换教室

2、NOIP2009最优贸易

NOIP专题复习1 图论-最短路的更多相关文章

  1. NOIP专题复习3 图论-强连通分量

    目录 一.知识概述 二.典型例题 1.[HAOI2006]受欢迎的牛 2.校园网络[[USACO]Network of Schools加强版] 三.算法分析 (一)Tarjan算法 (二)解决问题 四 ...

  2. NOIP专题复习2 图论-生成树

    目录 一.知识概述 二.典型例题 1.口袋的天空 三.算法分析 (一)Prim算法 (二)Kruskal 四.算法应用 1.[NOIP2013]货车运输 五.算法拓展 1977: [BeiJing20 ...

  3. 倍增&矩阵乘法 专题复习

    倍增&矩阵乘法 专题复习 PreWords 这两个基础算法我就不多说啦,但是还是要介绍一下" 广义矩阵 "乘法 其实就是把矩阵换成取\(max\),然后都一样... 据神仙 ...

  4. 状压dp专题复习

    状压dp专题复习 (有些题过于水,我直接跳了) 技巧总结 : 1.矩阵状压上一行的选择情况 \(n * 2^n\) D [BZOJ2734][HNOI2012]集合选数 蒻得不行的我觉得这是一道比较难 ...

  5. NOIp 图论算法专题总结 (1):最短路、最小生成树、最近公共祖先

    系列索引: NOIp 图论算法专题总结 (1) NOIp 图论算法专题总结 (2) NOIp 图论算法专题总结 (3) 最短路 Floyd 基本思路:枚举所有点与点的中点,如果从中点走最短,更新两点间 ...

  6. NOIp知识点复习——最短路计数

    $Mingqi\_H$ NOIp 2017考挂了...gg 重新开始好了. 计划明年2月24号前复习完所有的NOIp知识点(毕竟很不熟练啊),之后到七月底前学习完省选的东西(flag?). 从现在开始 ...

  7. D1图论最短路专题

    第一题:poj3660 其实是Floyed算法的拓展:Floyd-Wareshall.初始时,若两头牛关系确定则fij = 1. 对于一头牛若确定的关系=n-1,这说明这头牛的排名是确定的. 通过寻找 ...

  8. 图论最短路——spfa

    今天开始图论的最短路的最后复习,今天自己手打spfa虽然看了一眼书,但是也算是自己打出来的,毕竟很久没打了,而且还是一遍a代码下来15min左右就搞完了,成就感++.所以呢来篇博客记录一下. 香甜的黄 ...

  9. HDU 5521 [图论][最短路][建图灵感]

    /* 思前想后 还是决定坚持写博客吧... 题意: n个点,m个集合.每个集合里边的点是联通的且任意两点之间有一条dis[i]的边(每个集合一个dis[i]) 求同时从第1个点和第n个点出发的两个人相 ...

随机推荐

  1. 【算法小总结】LCS问题&&HDU1243

    LCS问题,又称最长公共子序列问题,是DP中较简单的一种,今天我们就来简单讲解一下. 设s1:AEGLEGLLELGEL 设s2:LREGELGEGLEG 求两个字符串的最大公共子序列长度 输出:8 ...

  2. python 面向对象八 多继承

    python是支持多继承的,在设计类的继承关系时,通常,主线都是单一继承下来的.但是,如果需要“混入”额外的功能,通过多重继承就可以实现,这种设计通常称之为MixIn. 为了更好地看出继承关系,以Mi ...

  3. bzoj 303: [CQOI2009]中位数图【前缀和+瞎搞】

    处理出一个序列c,a[i]>b,c[i]=1;a[i]==b,c[i]=0;a[i]<b,c[i]=-1,然后s为c的前缀和,设w为b在a序列里的下标 注意到子序列一定横跨w,并且一个符合 ...

  4. 洛谷 P4009 汽车加油行驶问题 【最小费用最大流】

    分层图,建k层,设(i,j,0)为点(i,j)的满油状态,全图的流量都是1,因为重复走到一个点没有意义.如果当前点是加油站,那么它向它上左的点连费用为a的边,向下右连费用为a+b的边: 否则,这个点的 ...

  5. 洛谷P3250 [HNOI2016]网络(整体二分+树状数组+树剖)

    传送门 据说正解是树剖套堆???然而代码看着稍微有那么一点点长…… 考虑一下整体二分,设当前二分到的答案为$mid$,如果所有大于$mid$的边都经过当前点$x$,那么此时$x$的答案必定小于等于$m ...

  6. Ubuntu An error occurred,please run Package Manager..

    转自https://blog.csdn.net/idealcitier/article/details/78294137 An error occurred,please run Package Ma ...

  7. 图的遍历---------开始开始-------o(∩_∩)o 哈哈

    图的遍历 深度优先搜索(Depth First Search , DFS) --深度优先搜索--我的理解就是分身术的另一种实现方法---用分身术将所有能看到的路都走一遍----这就是深度搜索--- 下 ...

  8. _bzoj1061 [Noi2008]志愿者招募【最小费用最大流】

    传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=1061 尽管不是mcmf的裸题,但还是保存一下模版叭~ 很好的一道建模的题,把变量间的加加减减 ...

  9. python 字符串的split()函数详解(转)

    作者:宋桓公 出处:http://www.cnblogs.com/douzi2/ 如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的“推荐”将是我最大的写作动力!欢迎各位转载,但是未经作者本人同 ...

  10. Jquery插件jqprint-0.3.js实现打印

    1.首先引用Jquery和jqprint-0.3.js(依赖于Jquery的) <script language="javascript" src="jquery- ...