NOIP专题复习1 图论-最短路
一、知识概述
今天我们要复习的内容是图论中的最短路算法,我们在这里讲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 图论-最短路的更多相关文章
- NOIP专题复习3 图论-强连通分量
目录 一.知识概述 二.典型例题 1.[HAOI2006]受欢迎的牛 2.校园网络[[USACO]Network of Schools加强版] 三.算法分析 (一)Tarjan算法 (二)解决问题 四 ...
- NOIP专题复习2 图论-生成树
目录 一.知识概述 二.典型例题 1.口袋的天空 三.算法分析 (一)Prim算法 (二)Kruskal 四.算法应用 1.[NOIP2013]货车运输 五.算法拓展 1977: [BeiJing20 ...
- 倍增&矩阵乘法 专题复习
倍增&矩阵乘法 专题复习 PreWords 这两个基础算法我就不多说啦,但是还是要介绍一下" 广义矩阵 "乘法 其实就是把矩阵换成取\(max\),然后都一样... 据神仙 ...
- 状压dp专题复习
状压dp专题复习 (有些题过于水,我直接跳了) 技巧总结 : 1.矩阵状压上一行的选择情况 \(n * 2^n\) D [BZOJ2734][HNOI2012]集合选数 蒻得不行的我觉得这是一道比较难 ...
- NOIp 图论算法专题总结 (1):最短路、最小生成树、最近公共祖先
系列索引: NOIp 图论算法专题总结 (1) NOIp 图论算法专题总结 (2) NOIp 图论算法专题总结 (3) 最短路 Floyd 基本思路:枚举所有点与点的中点,如果从中点走最短,更新两点间 ...
- NOIp知识点复习——最短路计数
$Mingqi\_H$ NOIp 2017考挂了...gg 重新开始好了. 计划明年2月24号前复习完所有的NOIp知识点(毕竟很不熟练啊),之后到七月底前学习完省选的东西(flag?). 从现在开始 ...
- D1图论最短路专题
第一题:poj3660 其实是Floyed算法的拓展:Floyd-Wareshall.初始时,若两头牛关系确定则fij = 1. 对于一头牛若确定的关系=n-1,这说明这头牛的排名是确定的. 通过寻找 ...
- 图论最短路——spfa
今天开始图论的最短路的最后复习,今天自己手打spfa虽然看了一眼书,但是也算是自己打出来的,毕竟很久没打了,而且还是一遍a代码下来15min左右就搞完了,成就感++.所以呢来篇博客记录一下. 香甜的黄 ...
- HDU 5521 [图论][最短路][建图灵感]
/* 思前想后 还是决定坚持写博客吧... 题意: n个点,m个集合.每个集合里边的点是联通的且任意两点之间有一条dis[i]的边(每个集合一个dis[i]) 求同时从第1个点和第n个点出发的两个人相 ...
随机推荐
- pom.xml内容没有错,但一直报错红叉 解决办法
转自:http://www.cnblogs.com/sxdcgaq8080/p/5590254.html [maven] pom.xml内容没有错,但一直报错红叉 解决办法 1.首先看一下下面的这两个 ...
- 「vijos」lxhgww的奇思妙想(长链剖分)
传送门 长链剖分的板子(又是乱搞优化暴力) 对于每一个点,我们定义它深度最深的子节点为它的重儿子(为什么不叫长儿子……),他们之间的连边为重边 然后长链剖分有几个性质 1.总链长为$O(n)$ 2.一 ...
- [App Store Connect帮助]七、在 App Store 上发行(3.4)提交至“App 审核”:将构建版本从审核中移除
若要停止“App 审核”流程,您可以将该 App 版本从 App 审核中移除.要执行此项操作,App 状态必须为下列之一: 正在等待出口合规检查 正在等待审核 正在审核 等待开发者发布 等待 Appl ...
- 关于element-ui的diallog拖动的实现
先给下载地址 https://files.cnblogs.com/files/maruihua/el-dragDialog.rar 需要注意的是如果给模态框加入拖动指令,一些定位样式会出问题,需谨慎 ...
- 微信支付接口调用问题(android正常,iphone调不起)
转自:http://blog.csdn.net/tt123123/article/details/53897035 碰到的问题 :根据微信提供的示例代码(ASP.NET),配置好一切后, 用andro ...
- 给Ambari集群里安装可视化分析利器工具Hue步骤(图文详解)
扩展博客 以下,是我在手动的CDH版本平台下,安装Hue. CDH版本大数据集群下搭建Hue(hadoop-2.6.0-cdh5.5.4.gz + hue-3.9.0-cdh5.5.4.tar.gz) ...
- 命名管道实现进程间通信--石头、剪刀、布游戏 分类: linux 2014-06-01 22:50 467人阅读 评论(0) 收藏
下面这个程序利用命名管道实现进程间通信,模拟石头剪刀布游戏. 主进程为裁判进程,两个子进程为选手进程.裁判与选手间各建立一个命名管道. 进行100次出招,最后给出游戏胜负. #include < ...
- Android Dialogs(2)最好用DialogFragment创建Dialog
Creating a Dialog Fragment You can accomplish a wide variety of dialog designs—including custom layo ...
- 453 Minimum Moves to Equal Array Elements 最小移动次数使数组元素相等
给定一个长度为 n 的非空整数数组,找到让数组所有元素相等的最小移动次数.每次移动可以使 n - 1 个元素增加 1.示例:输入:[1,2,3]输出:3解释:只需要3次移动(注意每次移动会增加两个元素 ...
- vue2.0 动态切换组件
组件标签是Vue框架自定义的标签,它的用途就是可以动态绑定我们的组件,根据数据的不同更换不同的组件. <!DOCTYPE html> <html lang="en" ...