一、知识概述

今天我们要复习的内容是图论中的最短路算法,我们在这里讲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. 【WIP】rails redis的使用

    创建: 2018/03/17 [任务表]TODO 注: mac版 %开头表示命令行命令 安装    安装 % brew install redis  确认版本 % redis-server --ver ...

  2. E20170521-ts

    redirect vt. 使改寄,更改(信件等)姓名地址; 改变方向,改变线路; 重新寄送; teletype   n. 电传打字机,电报交换机,打字电报通讯; descriptor n. 描述符; ...

  3. app 后台程序设计

    限制客户端一分钟之内访问接口的次数 1.设备的唯一标识获取这个实际上IOS7后会存在问题,权限已经收回了,android可以2.唯一标识可以通过生成一个token区分3.每分钟的频率可以这样设置 ke ...

  4. C++this详解

    以前对this指针误解挺多的,在这里单独写一篇进行总结,有不对之处,欢迎指正批评! 一.问题 1.一个类中的不同对象在调用自己的成员函数时,其实它们调用的是同一段函数代码,那么成员函数如何知道要访问哪 ...

  5. Spring Json数据

    json 数据转换 @RequestBody 用于读取Request请求的body部分数据,使用系统默认配置的HttpMessageConverter 进行解析,然后把相应的数据绑定到Controll ...

  6. 构造 HDOJ 5400 Arithmetic Sequence

    题目传送门 题意:问有多少个区间,其中存在j使得ai + d1 == ai+1(i<j) && ai + d2 == ai+1 (i>j) 构造:用c1[i], c2[i] ...

  7. 设置当前导航栏(navigationController)的标题

    一般在有导航navigationController的情况下,要设置页面的标题很简单 self.title = @"测试"; 也可以 self.navigationItem.tit ...

  8. vijos P1412多人背包 DP的前k优解

    https://vijos.org/p/1412 把dp设成,dp[i][v][k]表示在前i项中,拥有v这个背包,的第k大解是什么. 那么dp[i][v][1...k]就是在dp[i - 1][v] ...

  9. Laravel环境搭建

    在有了初步认知后,当然就要开始在自己的电脑上搭建Laravel的开发环境了. 系统环境需求 PHP 5.3.7或者更高版本,如果没有系统没有安装PHP环境的,请到下面地址下载:http://cn2.p ...

  10. 【开源】基于EF6+MVC5+API2+Easyui1.4.5+Easyui管理模板开发的管理系统

    经过近一步完善调整,现将本系统源码正式开放,定名为:EasyuiAdminFramework,另外EasyuiAdminTemplate及EasyuiFlatTheme也一并开源 项目主页:http: ...