首先来个板子,单源最短路径,Dijkstra算法。
杭电2544
题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=2544
Dijkstra需要的数据结构有4个数组,在代码中说明了每个数据结构的用途。
首先把第起点放入S集合中,然后找出不在S集合中的离起点最近的点,将其加入到S集合中。再对dis数组进行更新,看是否经过该点能够使路径长度变短。直到将所有点都加入到S集合中。
代码如下:
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
#define maxNum = 0x3f3f3f3f
int f[101][101]; //矩阵,i到j的cost
int dis[101]; //存储从1到i的最短路径长度,即时更新
int s[101]; //S集合
int path[101]; //存储从1到n的最短路径所经过的点,便于输出路径
int main()
{
int m, n;
int a, b, c;
while (true){
scanf("%d%d", &n, &m);
if (m == 0 && n == 0) break;
memset(f, 0x3f, sizeof(f)); //初始化矩阵为最大值
memset(s, 0, sizeof(s));
for (int i = 0; i < m; i++){
scanf("%d%d%d", &a, &b, &c);
f[a][b] = c;
f[b][a] = c;
}
for (int i = 1; i <= n; i++){
dis[i] = f[1][i];
}
dis[1] = 0; s[1] = 1;
for (int i = 1; i <= n; i++){
int minnum = 0x3f3f3f3f;
int u = 1;//找到不在S集合中,离S集合最近的点
for (int j = 1; j <= n; j++){
if (!s[j] && minnum > dis[j]){
u = j;
minnum = dis[j];
}
}
s[u] = 1; //把最近的那个点,加进集合S
for (int j = 1; j <= n; j++){ //更新
if (!s[j] && dis[u] + f[u][j] < dis[j]){
dis[j] = dis[u] + f[u][j];
path[j] = u;
}
}
}
printf("%d\n", dis[n]);
}
return 0;
}
flod算法,多源最短路径算法,核心只有5行代码,而且只需要一个矩阵数组即可。
POJ1125
原题链接: http://poj.org/problem?id=1125
这题意太难理解了,看了好几遍才看明白。
大意是:有n个股票经纪人,输入信息中有各经纪人之间的消息传递时间。要从某一个经纪人传出消息去,使所有人都能接收到消息。题目要求输出从哪个经纪人作为起点传出消息去,能使所有人都能在最短时间内接收到消息。
用flod计算任意两点之间的最短路径,然后对第i行,找出最大时间来t,这样可以确保第i个人传递消息时,经过t分钟后可以使所有人收到消息。然后对这i个时间t求最小值,同时记录下对应的i。就找到了起点和最短时间。
下面是AC代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include "stdio.h"
using namespace std;
int a[101][101];
int main(){
int n;
while (true){
scanf("%d", &n);
if (n == 0) break;
memset(a, 0x3f, sizeof(a));
for (int i = 1; i <= n; i++){
int m, x, y;
scanf("%d", &m);
for (int j = 1; j <= m; j++){
scanf("%d%d", &x, &y);
a[i][x] = y;
}
}//输入
for (int k = 1; k <= n; k++){
for (int i = 1; i <= n; i++){
for (int j = 1; j <= n; j++){
if (a[i][j] > a[i][k] + a[k][j])
a[i][j] = a[i][k] + a[k][j];
}
}
}//flod算法结束
//下面寻找最小的那个时间,及对应的人
int ans = 0x3f3f3f3f, person, ans2;
//ans2表示每一行中最大的,ans表示所有行中最小的,即ans2中的最小值。
for (int i = 1; i <= n; i++){
ans2 = 0;
for (int j = 1; j <= n; j++){
if (i == j) continue;
ans2 = max(a[i][j], ans2);
}
if (ans > ans2){
ans = ans2;
person = i;
}
}
printf("%d %d\n", person, ans);
}
return 0;
}
除了上面两种最短路径,还有bellman-ford算法。这个是为了解决Dijkstra算法只能计算非负权值的情况。它在Dijkstra算法的基础上,增加了判断是否存在负环路,如果存在,则肯定不会有最短路径,不存在即可输出最短路径。
如果不存在负环路,那么经过n-1遍更新后,进行第n遍操作时最短路径的数组dis[]肯定不会发生更改,但是如果发生了变化,说明存在负环。核心代码如下:
bool Bellman_Ford()
{
for(int i = 1; i <= nodenum; ++i) //初始化
dis[i] = (i == original ? 0 : MAX);
for(int i = 1; i <= nodenum - 1; ++i) //从这里可以看出复杂度是n*m,采用的是结构体数组存储。
for(int j = 1; j <= edgenum; ++j)
if(dis[edge[j].v] > dis[edge[j].u] + edge[j].cost) //进行松弛更新操作
{
dis[edge[j].v] = dis[edge[j].u] + edge[j].cost;
pre[edge[j].v] = edge[j].u; //该数组记录的是最短路径的前一个节点编号
}
bool flag = 1; //判断是否含有负权回路
for(int i = 1; i <= edgenum; ++i)
if(dis[edge[i].v] > dis[edge[i].u] + edge[i].cost)
{
flag = 0;
break;
}
return flag;
}