hdu 2544最短路——最短路的初次总结 UESTC 6th Programming Contest Online
这是一道标准的模板题,所以拿来作为这一段时间学习最短路的总结题目。
题意很简单:
有多组输入数据,每组的第一行为两个整数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的更多相关文章
- HDU 2544 单源最短路
题目链接: 传送门 最短路 Time Limit: 1000MS Memory Limit: 65536K 题目描述 在每年的校赛里,所有进入决赛的同学都会获得一件很漂亮的t-shirt.但是 ...
- HDU 2544(简单最短路)
http://acm.hdu.edu.cn/showproblem.php?pid=2544 /* 使用pair代替结构 */ #include <iostream> #include & ...
- hdu 2544 最短路 Dijkstra
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2544 题目分析:比较简单的最短路算法应用.题目告知起点与终点的位置,以及各路口之间路径到达所需的时间, ...
- UESTC 30 &&HDU 2544最短路【Floyd求解裸题】
最短路 Time Limit: 5000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submiss ...
- HDU 2544最短路 (迪杰斯特拉算法)
传送门: http://acm.hdu.edu.cn/showproblem.php?pid=2544 最短路 Time Limit: 5000/1000 MS (Java/Others) Me ...
- 最短路 HDU 2544
最短路 Time Limit: 5000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submiss ...
- HDU 2544 最短路(邻接表+优先队列+dijstra优化模版)
最短路 Time Limit: 5000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submiss ...
- hdu 2544 最短路 (最短路径)
最短路 Time Limit: 5000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submiss ...
- HDU 2544.最短路-最短路(Dijkstra)
本来不想写,但是脑子不好使,还是写一下备忘_(:з」∠)_ 最短路 Time Limit: 5000/1000 MS (Java/Others) Memory Limit: 32768/3276 ...
随机推荐
- C#委托及事件
转载:http://www.cnblogs.com/warensoft/archive/2010/03/19/1689806.html C#委托及事件 在C#中,委托(delegate)是一种引用类型 ...
- Android 解决ListView中每一项与button冲突
在listView的item里面如果有button,ImageButton等控件,会使得ListView不会被点击,解决方法是: ①在Button上面添加属性 android:focusable=&q ...
- lintcode 中等题:Intersection of Two Linked Lists 两个链表的交叉
题目 两个链表的交叉 请写一个程序,找到两个单链表最开始的交叉节点. 样例 下列两个链表: A: a1 → a2 ↘ c1 → c2 → c3 ↗ B: b1 → b2 → b3 在节点 c1 开始交 ...
- 【Linux高频命令专题(20)】du
概述 显示每个文件和目录的磁盘使用空间. 命令格式 du [选项][文件] 文件缺省就代表当前目录大小 参数 -a或-all 显示目录中个别文件的大小. -b或-bytes 显示目录或文件大小时,以b ...
- 缓存初解(四)---Ibatis的缓存配置+Ehcache
项目完结,整理一些技术方面的相关收获. 已经记不得EhCacheController这个实现类最早来自于那里了,总之稍加修改后非常有效果,大家就这么用了,感谢最初开源的那位兄弟.这里,主要是做个记录, ...
- 写个Python练练手吧
在Python的交互式命令行写程序,好处是一下就能得到结果,坏处是没法保存,下次还想运行的时候,还得再敲一遍. 所以,实际开发的时候,我们总是使用一个文本编辑器来写代码,写完了,保存为一个.py文件, ...
- Java虚拟机的内存组成以及堆内存介绍
一.java内存组成介绍:堆(Heap)和非堆(Non-heap)内存 按照官方的说法:“Java 虚拟机具有一个堆,堆是运行时数据区域,所有类实例和数组的内存均从此处分配.堆是在 Java 虚拟机启 ...
- python中的MySQL数据库操作 连接 插入 查询 更新 操作
MySQL数据库 就数据库而言,连接之后就要对其操作.但是,目前那个名字叫做qiwsirtest的数据仅仅是空架子,没有什么可操作的,要操作它,就必须在里面建立“表”,什么是数据库的表呢?下面摘抄自维 ...
- Git使用简介
git创建分支并直接切换到分支:git checkout -b name git提交分支到远程服务器: git push origin name/git push origin name:name ...
- @interface java注解
@Documented,@Retention,@Target,@Inherited 1. 编写自定义@Todo注解经常我们在写程序时,有时候有些功能在当前的版本中并不提供,或由于某些其它原因,有些方法 ...