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

题意很简单:

有多组输入数据,每组的第一行为两个整数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. hdu 1370 Biorhythms

    中国剩余定理……. 链接http://acm.hdu.edu.cn/showproblem.php?pid=1370 /**************************************** ...

  2. jQuery从主页面存取控制 iframe 中的元素,参数及方法

    从主页面上获取iframe下的某个对象,或使用iframe下的方法,或是获取iframe下某个doc元素,要求兼容各类浏览器,不仅仅ie; $(function() { $('#abgne_ifram ...

  3. Android 设置界面的圆角选项

    请先看一个图: 像这种界面的设计,其实是这样的:整体为一个LinearLayout,一个一个排下去,每一个点击项都是一个RelativeLayout(要为其设置clickable为true),分为左右 ...

  4. no such partition grub rescue>

    事出有因: 电脑系统是win7+ubuntu,然后在win7下把ubuntu的分区给删除了,重启,出现 no such partition grub rescue> 错误. 原因是双系统之前是由 ...

  5. 【mysql的编程专题①】流程控制与其他语法

    流程控制与内置函数,一般用在select的field字段上,或者用在函数,存储过程,触发器中; 如果用在select上就会随着query出来的row来隐式迭代; 注释与语句结束符 语句结束符 默认有两 ...

  6. JAVA学习路线图---(JAVA1234)

    第一阶段-Java基础   这一阶段很重要,关系到你后面阶段的学习,所以务必把这一阶段掌握好: 如果你是0基本,我推荐一本比较好的,适合初学者看的书:明日科技的<Java从入门到精通>,最 ...

  7. [Unity菜鸟] Character控制移动

    1. 给角色加角色控制器组件,然后用以下代码可以控制角色移动和跳跃 float speed = 6.0f; float jumpSpeed = 8.0f; float gravity = 20.0f; ...

  8. JavaWeb项目开发案例精粹-第2章投票系统-004action层

    1. package com.sanqing.action; import java.util.UUID; import com.opensymphony.xwork2.ActionSupport; ...

  9. GuessNumber

    import java.util.*; public class GuessNumber { public static void main(String[] args) { int num = ne ...

  10. 基于nginx的tomcat负载均衡和集群

    要集群tomcat主要是解决SESSION共享的问题,因此我利用memcached来保存session,多台TOMCAT服务器即可共享SESSION了. 你可以自己写tomcat的扩展来保存SESSI ...