进入图之后,最短路径可谓就是一大重点,最短路径的求法有很多种,每种算法各有各的好处,你会几种呢?下面来逐个讲解。

1 floyed算法

  1)明确思想及功效:在图中求最短路还是要分开说的,分别是单源最短路和多源最短路,而floyed算法是求多源最短路的,什么是多源最短路呢?简单来说就是用完算法之后能直接写出任意两点间的最短路径长度。floyed算法在本质上是动态规划思想,不断更新最短路径的值,主要思想就是不断判断两个点是否可以通过一个点中继以刷新当前两个点最短路径的估计值,直到每两个点都判断完成。很容易理解,就不详细图解了。

  2)代码:

     for(int k=;k<=n;k++)
for(int i=;i<=n;i++)
for(int j=;j<=n;j++)
{
if(a[i][j]>a[i][k]+a[k][j])
{
a[i][j]=a[i][k]+a[k][j];
} }

  3)时间复杂度:O(n^3)

2 dijkstra算法

  1)思想及其功效:dijkstra算法属于单源最短路算法,简单说就是用过之后,只能直接写出一点到其他任意一点的最短路径长度。这个算法类似于prim算法,找出刷新点所能到达的点,然后再继续刷新,写起来太难理解,自己都不会说了,那就具体以一幅图来讲解。

  如果这个图是这样的:

  当然,图要用矩阵来存储。因为是单源最短路,只能求一个点到其他各点的最短距离,所以我们可以假设是求1号点到其他各点的最短距离。此时我们需要一个数组dis[ ]来存储1到5个顶点分别的最短路径长度。那么,在最开始时,数组里初始化为估计值,凡是1能到达的点,相应路径的长度会直接存在数组中,而不能直接到达的点则被当做正无穷,可以赋值成999999或0x3f3f3f3f等尽可能大的数,具体是什么样呢?如图所示:

       

  思考几个问题:

  •   为什么dis[1]的值是0?

    answer:因为dis[ ]数组所存的是1到各点的最短路径长度,1到1不用走,直接就到了呀,所以是0;

  •   OO是什么?

    answer:因为画图软件不会画无限符号,就用这个代替正无穷吧,写代码时可以赋成尽可能大的值。

  •   为什么有的赋值了而有的没有?

    answer:如上面所说,比如2和5,从图上看它们都和1有直接连边,可以赋进dis里,但是3和4与1无直接连边,只能通过其他点间接到达,所以先赋估计值正无穷。

  接着就把刚才刷新的2和5作为中继看一看是否可以刷新其他点的值。

  先来看一看通过2所能刷新的点,分别是3和5,dis[3]原本是正无穷,现在通过2只需要权值5就能到达,比正无穷小,就更新它,像这种操作,专业术语叫做“松弛”,同理,继续松弛操作,dis[5]的值本来为8,因为通过2来中继,3+4<8,所以更新它,继续这样操作不断循环下去,直到所有未知点全部被访问并进行松弛操作。

//蓝边表示中继,红边代表这条边不能用,绿边表示可以通过这条边来刷新。

  刚才刷新了3和4,现在通过3和4来中继。

  通过3无法继续刷新,继续看4。

  依旧没点刷新,此时,所有点都访问过了,并进行了松弛操作所有的dis[ ]全部从估计值一步步变成了确定值。

  算法演示结束。

  2)主要问题

  •   怎么确定所有顶点被访问过?

  answer:可以定义一个vis[ ]数组,最开始全是0,每选出一个点,就把vis的值更新为1。

  •   先找哪个点为中继?

  answer:先选一条没有访问过且dis值最小的。

  •   有代码吗?

  answer:有,如下:

 void dijkstra()
{ vis[]==;dis[]=;
for(int i=;i<=n-;i++)
{
minn=inf;
for(int j=;j<=n;j++)
{
if(vis[j]==&&dis[j]<minn)
{
k=j;
minn=dis[j];
}
}
vis[k]=;
for(int j=;j<=n;j++)
{
if(dis[j]>dis[k]+map[k][j]&&map[k][j]<inf)
dis[j]=dis[k]+map[k][j];
}
}
}

   3)时间复杂度:O(n^2 )

3 SPFA

  图的权值有正的,当然也有负的,对于负边权,之前学过的算法都无法处理,而SPFA则能处理负权边。思路和dijkstra有点像,具体图解,想看点这里【最短路径】 SPFA算法。先放个代码凑个数:

void SPFA()
{
for(int i=;i<=n;i++)
dis[i]=inf;
queue<int>q;
q.push();vis[]=;dis[]=;
while(q.size())
{
x=q.front();q.pop();vis[x]=;
for(int i=head[x];i;i=a[i].next)
{
int s=a[i].to;
if(dis[s]>dis[x]+a[i].cost)
{
dis[s]=dis[x]+a[i].cost;
if(vis[s]==)
{
vis[s]=;
q.push(s);
}
}
}
}
}

4 例题讲解:

1376:信使(msner)

时间限制: 1000 ms         内存限制: 65536 KB
提交数: 1847     通过数: 879

【题目描述】

战争时期,前线有n个哨所,每个哨所可能会与其他若干个哨所之间有通信联系。信使负责在哨所之间传递信息,当然,这是要花费一定时间的(以天为单位)。指挥部设在第一个哨所。当指挥部下达一个命令后,指挥部就派出若干个信使向与指挥部相连的哨所送信。当一个哨所接到信后,这个哨所内的信使们也以同样的方式向其他哨所送信。直至所有n个哨所全部接到命令后,送信才算成功。因为准备充足,每个哨所内都安排了足够的信使(如果一个哨所与其他k个哨所有通信联系的话,这个哨所内至少会配备k个信使)。

现在总指挥请你编一个程序,计算出完成整个送信过程最短需要多少时间。

【输入】

第1行有两个整数n和m,中间用1个空格隔开,分别表示有n个哨所和m条通信线路,且1≤n≤100。

第2至m+1行:每行三个整数i、j、k,中间用1个空格隔开,表示第i个和第j个哨所之间存在通信线路,且这条线路要花费k天。

【输出】

一个整数,表示完成整个送信过程的最短时间。如果不是所有的哨所都能收到信,就输出-1。

【输入样例】

4 4
1 2 4
2 3 7
2 4 1
3 4 6

【输出样例】

11 

  这道题一看感觉没思路,就盲目的先写一遍floyed,再看看有没有什么规律,写着写着就想通了,既然每两个哨所之间都可以直接/间接的通信,而且起点是从第一个哨所开始的,那么整个时间当然就是1号哨所到达其他哨所的最短路径中最大的,应为其他路径在执行此路径时也在同时进行,废话不多说,代码如下:

//floyed 版1376:信使(msner)

 1 #include<iostream>
2 using namespace std;
3 int a[1000][1000],n,m,d,b,c;int maxn=-1;
4 int main()
5 {
6 cin>>n>>m;
7 for(int i=1;i<=n;i++)
8 for(int j=1;j<=n;j++)//初始赋成尽可能大的值
9 a[i][j]=999999;
10 for(int i=1;i<=n;i++)
11 a[i][i]=0;
12 for(int i=1;i<=m;i++)
13 {
14 cin>>d>>b>>c;
15 a[d][b]=c;
16 a[b][d]=c;
17 }
18 for(int k=1;k<=n;k++)
19 for(int i=1;i<=n;i++)
20 for(int j=1;j<=n;j++)
21 {
22 if(a[i][j]>a[i][k]+a[k][j])//floyed算法
23 {
24 a[i][j]=a[i][k]+a[k][j];
25 }
26
27 }
28 for(int i=1;i<=n;i++)
29 {
30 if(maxn<a[1][i])//选出最大的路径
31 maxn=a[1][i];
32 }
33 cout<<maxn;
34 return 0;
35 }

//dijksra版1376:信使(msner)

 #include<iostream>
#define inf 999999
using namespace std;
int n,m,map[][],dis[],vis[],a,b,maxn=-,minn,k;
inline void dijkstra()
{ vis[]==;dis[]=;
for(int i=;i<=n-;i++)
{
minn=inf;
for(int j=;j<=n;j++)
{
if(vis[j]==&&dis[j]<minn)
{
k=j;
minn=dis[j];
}
}
vis[k]=;
for(int j=;j<=n;j++)
{
if(dis[j]>dis[k]+map[k][j]&&map[k][j]<inf)
dis[j]=dis[k]+map[k][j];
}
}
}
int main()
{
cin>>n>>m;
for(int i=;i<=n;i++)
{
for(int j=;j<=n;j++)
map[i][j]=inf;
dis[i]=inf;
}
for(int i=;i<=m;i++)
{
cin>>a>>b;
cin>>map[a][b];
map[b][a]=map[a][b];
}
for(int i=;i<=n;i++)
dis[i]=map[][i];
dijkstra();
for(int i=;i<=n;i++)
{
if(dis[i]>maxn)
maxn=dis[i];
}
cout<<maxn;
return ;
}

//SPFA版 1376:信使(msner)

 #include<iostream>
#include<cstdio>
#include<queue>
#define inf 999999
using namespace std;
struct map{
int from;
int to;
int cost;
int next;
}a[];
int head[],dis[],vis[],n,m,maxn=-,x,cnt,a1,b1,c1;
void add(int from,int to,int cost)
{
a[++cnt].to=to;
a[cnt].cost=cost;
a[cnt].next=head[from];
head[from]=cnt;
}
void SPFA()
{
for(int i=;i<=n;i++)
dis[i]=inf;
queue<int>q;
q.push();vis[]=;dis[]=;
while(q.size())
{
x=q.front();q.pop();vis[x]=;
for(int i=head[x];i;i=a[i].next)
{
int s=a[i].to;
if(dis[s]>dis[x]+a[i].cost)
{
dis[s]=dis[x]+a[i].cost;
if(vis[s]==)
{
vis[s]=;
q.push(s);
}
}
}
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=;i<=m;i++)
{
scanf("%d%d%d",&a1,&b1,&c1);
add(a1,b1,c1);
add(b1,a1,c1);
} SPFA();
for(int i=;i<=n;i++)
if(dis[i]>maxn&&dis[i]!=inf) maxn=dis[i];
printf("%d",maxn);
return ;
}

//SPFA+SLF版 1376:信使(msner)

 #include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#define inf 0x3f3f3f3f
using namespace std;
int dis[],head[]={},vis[]={},n,m,s,x,a,b,c,cnt,maxn;
struct edge{
int from;
int to;
int value;
int next;
}map[];
inline void add(int from,int to,int value)
{
map[++cnt].to=to;
map[cnt].value=value;
map[cnt].next=head[from];
head[from]=cnt;
}
void SPFA()
{
memset(dis,inf,sizeof(dis));
deque<int>q;
q.push_back();dis[]=;vis[]=;
while(q.size())
{
x=q.front();q.pop_front();vis[x]=;
for(int i=head[x];i;i=map[i].next)
{
s=map[i].to;
if(dis[s]>dis[x]+map[i].value)
{
dis[s]=dis[x]+map[i].value;
if(vis[s]==)
{
if(dis[s]<dis[q.front()]) q.push_front(s);
else q.push_back(s);
vis[s]=;
}
}
}
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=;i<=m;i++)
{
scanf("%d%d%d",&a,&b,&c);
add(a,b,c);
add(b,a,c);
}
SPFA();
for(int i=;i<=n;i++)
if(dis[i]>maxn) maxn=dis[i];
cout<<maxn;
return ;
}

//SPFA+LLL版 1376:信使(msner)

 #include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define inf 0x3f3f3f3f
using namespace std;
int dis[],head[]={},vis[]={},n,m,sum,x,y,a,b,c,cnt,p,s,maxn=-,cnt_2=;
struct edge{
int from;
int to;
int value;
int next;
}map[];
inline void add(int from,int to,int value)
{
map[++cnt].to=to;
map[cnt].value=value;
map[cnt].next=head[from];
head[from]=cnt;
}
void SPFA()
{
memset(dis,inf,sizeof(dis));
queue<int>q;
q.push();dis[]=;vis[]=;
while(q.size())
{
p=q.front();q.pop();
if(dis[p]*cnt_2>sum)
{
q.push(p);
continue;
}
sum-=dis[p];cnt_2--;
vis[p]=;;
for(int i=head[p];i;i=map[i].next)
{
s=map[i].to;
if(dis[s]>dis[p]+map[i].value)
{
dis[s]=dis[p]+map[i].value;
if(vis[s]==)
{
vis[s]==;
q.push(s);
cnt_2++;
sum+=dis[s];
}
}
}
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=;i<=m;i++)
{
scanf("%d%d%d",&a,&b,&c);
add(a,b,c);
add(b,a,c);
}
SPFA();
for(int i=;i<=n;i++)
if(maxn<dis[i]) maxn=dis[i];
printf("%d",maxn);
return ;
}

//SPFA+SLF+LLL版 1376:信使(msner)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define inf 0x3f3f3f3f
using namespace std;
int dis[],head[]={},vis[]={},n,m,sum,x,y,a,b,c,cnt,p,s,maxn=-,cnt_2=;
struct edge{
int from;
int to;
int value;
int next;
}map[];
inline void add(int from,int to,int value)
{
map[++cnt].to=to;
map[cnt].value=value;
map[cnt].next=head[from];
head[from]=cnt;
}
void SPFA()
{
memset(dis,inf,sizeof(dis));
deque<int>q;
q.push_back();dis[]=;vis[]=;
while(q.size())
{
p=q.front();q.pop_front();
if(dis[p]*cnt_2>sum)
{
q.push_back(p);
continue;
}
sum-=dis[p];cnt_2--;
vis[p]=;;
for(int i=head[p];i;i=map[i].next)
{
s=map[i].to;
if(dis[s]>dis[p]+map[i].value)
{
dis[s]=dis[p]+map[i].value;
if(vis[s]==)
{
vis[s]==;
if(dis[s]<dis[q.front()]) q.push_front(s);
else q.push_back(s);
cnt_2++;
sum+=dis[s];
}
}
}
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=;i<=m;i++)
{
scanf("%d%d%d",&a,&b,&c);
add(a,b,c);
add(b,a,c);
}
SPFA();
for(int i=;i<=n;i++)
if(maxn<dis[i]) maxn=dis[i];
printf("%d",maxn);
return ;
}

  写是确实写了六次,看看你会写几种?但是想着应该是越往后越快呀,后面有用到优化,可是结果堪忧啊。

//SPFA三种优化方法

//SPFA、dijkstra、floyed

  天哪!合着我这是优化的越来越慢了,看来优化也得慎重啊。下一期将会重点讲述如何优化SPFA。

【最短路径】 常用算法图解+1376:信使(msner)六解的更多相关文章

  1. 最短路径:Dijkstra & Floyd 算法图解,c++描述

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  2. ACM常用算法及练习(1)

    ACM常用算法及练习 第一阶段:练经典常用算法,下面的每个算法给我打上十到二十遍,同时自己精简代码,因为太常用,所以要练到写时不用想,10-15分钟内打完,甚至关掉显示器都可以把程序打出来. 1.最短 ...

  3. 【小白学游戏常用算法】二、A*启发式搜索算法

    在上一篇博客中,我们一起学习了随机迷宫算法,在本篇博客中,我们将一起了解一下寻路算法中常用的A*算法. 通常情况下,迷宫寻路算法可以使用深度优先或者广度优先算法,但是由于效率的原因,不会直接使用这些算 ...

  4. GJM : 数据结构 - 轻松看懂机器学习十大常用算法 [转载]

     转载请联系原文作者 需要获得授权,非法转载 原文作者将享受侵权诉讼 文/不会停的蜗牛(简书作者)原文链接:http://www.jianshu.com/p/55a67c12d3e9 通过本篇文章可以 ...

  5. ACM常用算法及练习(2)

    ACM常用算法及练习 知识类型 重要度 容易度 应掌握度 典型题 其他           数据结构(5) 链表 ★★☆ ★★★ ★★☆     栈 stack ★★★ ★★★ ★★★ HLoj120 ...

  6. javascript中数组的常用算法深入分析

    Array数组是Javascript构成的一个重要的部分,它可以用来存储字符串.对象.函数.Number,它是非常强大的.因此深入了解Array是前端必修的功课.本文将给大家详细介绍了javascri ...

  7. 轻松看懂机器学习十大常用算法 (Machine Learning Top 10 Commonly Used Algorithms)

    原文出处: 不会停的蜗牛    通过本篇文章可以对ML的常用算法有个常识性的认识,没有代码,没有复杂的理论推导,就是图解一下,知道这些算法是什么,它们是怎么应用的,例子主要是分类问题. 每个算法都看了 ...

  8. 图解 TCP/IP 第六章 TCP与UDP 笔记6.1 传输层的作用

     图解 TCP/IP  第六章 TCP与UDP   笔记6.1 传输层的作用   传输层必须指出这个具体的程序,为了实现这一功能,使用端口号这样一种识别码.根据端口号,就可以识别在传输层上一层的应用程 ...

  9. 总结Objective-c常用算法

          今天是星期天,想睡到10点起床,结果认为自己太奢侈了,不能这么做,于是把闹钟设置成了6:30:结果终于9:36醒了,起床,无缘无故迟了,好吧,就算太累了吧,周天就原谅自己一回.终于到了中午 ...

随机推荐

  1. 【设计模式】 模式PK:装饰模式VS适配器模式

    1.概述 装饰模式和适配器模式在通用类图上没有太多的相似点,差别比较大,但是它们的功能有相似的地方:都是包装作用,都是通过委托方式实现其功能.不同点是:装饰模式包装的是自己的兄弟类,隶属于同一个家族( ...

  2. leaflet一个前端gis框架,比openlayer更简单

    leaflet一个前端gis框架,比openlayer更简单 作者github:https://github.com/mourner?tab=overview&from=2009-12-01& ...

  3. AndroidStudio创建jinLibs文件夹

    在文件中的buildTypes节点下添加 sourceSets.main {          jniLibs.srcDir 'libs'      } 如图

  4. netcat、nc工具随记

    netcat又称nc工具,其最主要的作用就是建立连接并返回两个数据流,剩下的就看各位的想象力了,想象力是很重要的,这也是这个工具的强大之处的所在,所以重要的东西才要说三遍,想象力! 具体参数如下: - ...

  5. C++学习之路(七):以const,enum,inline替换#define

    这篇博文主要是编程中的一些问题和技巧.如题目所示,这些关键字的作用不再进行描述.直接描述功能和实例代码. 首先,在头文件中对类进行定义,是不会为类分配内存空间的,在这一点上类定义可以和普通变量类型的声 ...

  6. 命令行创建KVM虚拟机

    qemu命令创建虚拟机: qemu-img create -f qcow2 /home/ubuntu.img 20G   qemu-system-x86_64 -m 2048 -enable-kvm ...

  7. jQuery Validate插件 验证实例

    官网地址:http://bassistance.de/jquery-plugins/jquery-plugin-validation Validate手册: http://www.cnblogs.co ...

  8. java===java基础学习(8)---静态域与静态方法

    静态域:如果将域定义为static,每个类中只有一个这样的域.而每一个对象对于所有的实例域却都有自己的一份拷贝.例如,加入需要给每一个雇员赋予唯一的标识码.这里给的Employee类添加一个实例域id ...

  9. 64_g6

    gsettings-desktop-schemas-devel-3.24.0-1.fc26.x..> 22-Mar-2017 20:46 19386 gsf-sharp-0.8.1-27.fc2 ...

  10. ADO POST时出现“无法为更新定位行,一些值可能已在最后一次读取后已更改”问题的解决方法

    原因有这样几种: 1.在数据库设计时,为某些字段设置了默认值,在修改进行提交以后,数据库会自动修改对应字段的所有行的默认值,从而导致了数据库与数据集中数据的不一致,使ADOQuery无法对数据集进行定 ...