这是一道标准的模板题,所以拿来作为这一段时间学习最短路的总结题目。

题意很简单:

有多组输入数据,每组的第一行为两个整数n, m。表示共有n个节点,m条边。

接下来有m行,每行三个整数a, b, c。表示从a到b或从b到a长度为c。

求从1到n的最短路。

先说Floyd——

这个算法看上去就是一个三重for循环,然后在循环里不断对选择的两个节点进行松弛(感觉松弛这两个字很高端有没有)。

算法时间复杂度为O(n^3),n为节点数。所以一般可以用来处理规模1000以下的数据(即100数量级的,但是如果常数比较大的话也会超时,一般都是规模100的数据)。

但是,这是我目前掌握的唯一一个可以直接计算多源最短路的算法(即算法执行之后,获得图中任意两点间的最短路)。

代码如下:

 void Floyd()
{
for(int k = ; k <= n; k++) //选择的中间节点
for(int i = ; i <= n; i++) //选择的源节点,即出发点
for(int j = ; j <= n; j++) //选择的目标节点
if(mp[i][j] > mp[i][k] + mp[k][j])
mp[i][j] = mp[i][k]+mp[k][j]; //如果经过中间节点的路径(即i到k到j)小于直接从i到j,则进行更新操作(松弛)
}

这道题用floyd跑了41ms。

接着是Dijkstra——

Dijkstra的时间复杂度笼统的说是O(n^2), 精确一点是O(n^2+m), 如果源点可达,那么是O(n*lg n+m*lg n) => (m*lg n)。n是点数,m是边数。一般来说,规模10^4的数据是可以在1s内完成的。

Dijkstra可以用来求单源最短路,也就是从一个点出发,到其他所有点的最短路。

注意,当边存在负权的时候,Dijkstra就会求出错误答案了。

 void Dijkstra(int s)  //计算从s到其他所有点的最短路
{
for(int i = ; i <= n; i++) dis[i] = mp[s][i]; //维护一个记录最短路的数组
v[s] = ; //从s到s的最短路已获得,标记为1
int minn, k;
for(int i = ; i <= n; i++) //进行n次,可能小于n提前结束(好像是n-1次?)
{
minn = M;
for(int j = ; j <= n; j++) //寻找当前已经确定的最短路
{
if(!v[j] && minn > dis[j])
{
minn = dis[j];
k = j;
}
}
if(minn == M) break; //提前结束(即到所有点的距离都以找到,v[]全部为1),则提前退出 v[k] = ; //当前最短路已找到,标记为1
for(int j = ; j <= n; j++) //在当前最短路的基础上,寻找到其他未找到点的最短路
if(!v[j] && dis[j] > dis[k]+mp[k][j]) dis[j] = dis[k]+mp[k][j];
}
}

这道题用Dijkstra跑了15ms。

接下来是BellManFord算法——

这个算法就是进行连续松弛,时间复杂度为O(n*m)不能打折,所以效率比Dijkstra低。但是,不超过10^4的数据一般还是可以在1s内完成的

这个算法的优点是可以计算存在负权的图的最短路,同时它也是求单源最短路的算法。它还可以判断图中是否存在负环(存在负环时是没有最短路的)。

 bool BellManFord(int s)
{
for(int i = ; i <= n; i++) dis[i] = mp[s][i];
for(int i = ; i < n; i++)
{
for(int j = ; j <= n; j++)
{
for(int k = ; k <= n; k++)
{
if(dis[j] > dis[k]+mp[j][k]) dis[j] = dis[k]+mp[j][k];
}
}
}
for(int j = ; j <= n; j++) //算法的精髓,在求出一般意义的最短路之后,在进行一次判断操作,以确定是否存在负环
{
for(int k = ; k <= n; k++) if(dis[j] > dis[k]+mp[j][k]) return ; //存在负环,返回0
}
return ;
}

这个算法跑了31ms。

最后是Spfa算法——

这个算法是BellManFord算法的延伸,或者说优化。它的时间复杂度不稳定,不同的图可能算出来的不一样。我目前能写的是用队列的,类似于bfs,据说还有用dfs写的……

总之我对这个算法也不是完全掌握,只是能用而已。

如果Dijkstra也超时的话,可以用这个算法碰碰运气,说不定就过了。

对了,这个算法由于是从BellManFord优化而来的,所以也可以判负环,不过我这个没有写。加一个判断节点入队次数的操作就行了,如果某节点入队大于n次,就是有负环。

这个算法还是很好理解的。

 void Spfa(int s)
{
for(int i = ; i <= n; i++) dis[i] = M;
dis[s] = ;
queue<int>que;
que.push(s);
v[s] = ; int p;
while(!que.empty())
{
p = que.front();
que.pop();
v[p] = ;
for(int i = ; i <= n; i++)
{
if(dis[i] > dis[p]+mp[p][i])
{
dis[i] = dis[p]+mp[p][i];
if(v[i] == )
{
que.push(i);
v[i] = ;
}
}
}
} }

同样跑了15ms。

当然,以上的运行时间并不具有代表性,因为不同的图用不同的算法运行时间总存在差异,尤其是spfa,不过,某种程度上还是可以作为参考的。

整体代码:

 #include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std; const int M = ;
const int N = ; int n, m;
int a, b, c;
int mp[N][N];
bool v[N];
int dis[N]; void Init()
{
for(int i = ; i <= n; i++)
{
for(int j =; j < i; j++)
mp[i][j] = mp[j][i] = M;
mp[i][i] = ;
v[i] = ;
}
for(int i = ; i < m; i++)
{
scanf("%d%d%d", &a, &b, &c);
if(mp[a][b] > c) mp[a][b] = mp[b][a] = c;
}
} void Floyd()
{
for(int k = ; k <= n; k++) //选择的中间节点
for(int i = ; i <= n; i++) //选择的源节点,即出发点
for(int j = ; j <= n; j++) //选择的目标节点
if(mp[i][j] > mp[i][k] + mp[k][j])
mp[i][j] = mp[i][k]+mp[k][j]; //如果经过中间节点的路径(即i到k到j)小于直接从i到j,则进行更新操作(松弛)
} void Dijkstra(int s) //计算从s到其他所有点的最短路
{
for(int i = ; i <= n; i++) dis[i] = mp[s][i]; //维护一个记录最短路的数组
v[s] = ; //从s到s的最短路已获得,标记为1
int minn, k;
for(int i = ; i <= n; i++) //进行n次,可能小于n提前结束(好像是n-1次?)
{
minn = M;
for(int j = ; j <= n; j++) //寻找当前已经确定的最短路
{
if(!v[j] && minn > dis[j])
{
minn = dis[j];
k = j;
}
}
if(minn == M) break; //提前结束(即到所有点的距离都以找到,v[]全部为1),则提前退出 v[k] = ; //当前最短路已找到,标记为1
for(int j = ; j <= n; j++) //在当前最短路的基础上,寻找到其他未找到点的最短路
if(!v[j] && dis[j] > dis[k]+mp[k][j]) dis[j] = dis[k]+mp[k][j];
}
} bool BellManFord(int s)
{
for(int i = ; i <= n; i++) dis[i] = mp[s][i];
for(int i = ; i < n; i++)
{
for(int j = ; j <= n; j++)
{
for(int k = ; k <= n; k++)
{
if(dis[j] > dis[k]+mp[j][k]) dis[j] = dis[k]+mp[j][k];
}
}
}
for(int j = ; j <= n; j++) //算法的精髓,在求出一般意义的最短路之后,在进行一次判断操作,以确定是否存在负环
{
for(int k = ; k <= n; k++) if(dis[j] > dis[k]+mp[j][k]) return ; //存在负环,返回0
}
return ;
} void Spfa(int s)
{
for(int i = ; i <= n; i++) dis[i] = M;
dis[s] = ;
queue<int>que;
que.push(s);
v[s] = ; int p;
while(!que.empty())
{
p = que.front();
que.pop();
v[p] = ;
for(int i = ; i <= n; i++)
{
if(dis[i] > dis[p]+mp[p][i])
{
dis[i] = dis[p]+mp[p][i];
if(v[i] == )
{
que.push(i);
v[i] = ;
}
}
}
} } int main()
{
while(~scanf("%d%d", &n, &m) && (n+m))
{
Init();
//Floyd();
//printf("%d\n", mp[1][n]);
//Dijkstra(1);
//if(BellManFord(1))
Spfa();
printf("%d\n", dis[n]);
}
return ;
}

总之这只是一个入门等级的最短路,接下来还有一大波优化等着我们呢,几乎每个最短路算法都有优化,用优先队列,用堆,用双向队列……哦,天哪!

hdu 2544最短路——最短路的初次总结 UESTC 6th Programming Contest Online的更多相关文章

  1. HDU 2544 单源最短路

    题目链接: 传送门 最短路 Time Limit: 1000MS     Memory Limit: 65536K 题目描述 在每年的校赛里,所有进入决赛的同学都会获得一件很漂亮的t-shirt.但是 ...

  2. HDU 2544(简单最短路)

    http://acm.hdu.edu.cn/showproblem.php?pid=2544 /* 使用pair代替结构 */ #include <iostream> #include & ...

  3. hdu 2544 最短路 Dijkstra

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2544 题目分析:比较简单的最短路算法应用.题目告知起点与终点的位置,以及各路口之间路径到达所需的时间, ...

  4. UESTC 30 &&HDU 2544最短路【Floyd求解裸题】

    最短路 Time Limit: 5000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submiss ...

  5. HDU 2544最短路 (迪杰斯特拉算法)

    传送门: http://acm.hdu.edu.cn/showproblem.php?pid=2544 最短路 Time Limit: 5000/1000 MS (Java/Others)    Me ...

  6. 最短路 HDU 2544

    最短路 Time Limit: 5000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submiss ...

  7. HDU 2544 最短路(邻接表+优先队列+dijstra优化模版)

    最短路 Time Limit: 5000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submiss ...

  8. hdu 2544 最短路 (最短路径)

    最短路 Time Limit: 5000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submiss ...

  9. HDU 2544.最短路-最短路(Dijkstra)

    本来不想写,但是脑子不好使,还是写一下备忘_(:з」∠)_ 最短路 Time Limit: 5000/1000 MS (Java/Others)    Memory Limit: 32768/3276 ...

随机推荐

  1. java基础知识回顾之---java String final类普通方法的应用之“模拟字符串Trim方法”

    /* * 4,模拟一个trim功能一致的方法.去除字符串两端的空白  * 思路: * 1,定义两个变量. * 一个变量作为从头开始判断字符串空格的角标.不断++. * 一个变量作为从尾开始判断字符串空 ...

  2. ***解决PHP输出多余的空格或换行

    用CI框架写APP后台接口的时候,返回的JSON前面有多余的2哥换行,首先排查的是BOM,结果问题依旧 再就是排查<?php ?> 标签外没有多余的回车.换行,结果发现确实有多余的换行,去 ...

  3. 李洪强iOS开发之【Objective-C】09-空指针和野指针

    一.什么是空指针和野指针 1.空指针 1> 没有存储任何内存地址的指针就称为空指针(NULL指针) 2> 空指针就是被赋值为0的指针,在没有被具体初始化之前,其值为0. 下面两个都是空指针 ...

  4. 保障视频4G传输的流畅性,海康威视摄像头相关设置

    我们目前的rtsp视频解决方案如下: 摄像头<---------->NVR(通过4G上传)<---------->easydarwin<---------->自己的 ...

  5. 核心思想:百度网盘怎么盈利(互联网的高速更新决定了:亏钱你还有点机会,放弃连门都进不了),战略预备队 good

    百度做网盘很大程度就是为了防止别人依靠网盘做大和积累点技术储备.腾讯邮箱怎么赚钱?腾讯影音怎么赚钱?互联网的高速更新决定了,一些你看不起眼的软件很可能就会席卷整个市场,所以互联网大佬宁愿一些项目亏钱也 ...

  6. Android:储存方式之SharePreferences

    使用SharedPreferences保存数据,其实质是采用了xml文件存放数据, 存储位置:/data/data/<package name>/shared_prefs 写入: publ ...

  7. 查看Linux版本系统信息方法汇总

    Linux下如何查看版本信息, 包括位数.版本信息以及CPU内核信息.CPU具体型号等等,整个CPU信息一目了然.  1.# uname -a   (Linux查看版本当前操作系统内核信息)  Lin ...

  8. MyEclipse 利用反向功能生成Java 实体类

    1.Window -> Open Perspective -> MyEclipse Database Explorer 到DB Broswer界面 2.右键 -> New,新建一个数 ...

  9. MyEclipse +Servlet 乱码

    用MyEclipse 新建了Web Project,然后建立了一个Servlet,在doGet方法中谢了简单的一句欢迎语句,本来以为平平常常的事情,但是却出错了,乱码问题,在网上找了一些帖子,说是设置 ...

  10. Linux3.4内核的基本配置和编译

    转载自:http://www.embedu.org/Column/Column634.htm 作者:李昕,华清远见研发中心讲师. 了解Linux3.4内核的特性及新增功能,掌握Linux内核的编译过程 ...