首先是floyd算法,这种算法可以求出图中任意两点之间的最短距离,但是计算的时间复杂度较高,为O(n^3)。具体实现思路如下:
- 类似于动态规划,设置动态数组d[k][i][j],代表着在只允许经过前k个点时,点i到点j之间的最短路径。其中d[k][i][i]全为0,然后输入边长可以赋予给d[0][i][j]。
- 设置三重循环,对k,i,j进行迭代,每加入一个新的中间点k,那么d[k-1][i][k]+d[k-1][k][j]就是当路径中强行加入点k后的长度,若效果好于前一个最优路径d[k-1][i][j],则d[k][i][j]的最优路径就是它;否则维持上一个最优路径d[k-1][i][j]。
- 循环完成,得到任意两点之间的最优路径,输出d[n][1][n]。
核心代码如下:
for(int k=1;k<=nump;k++){
for(int i=1;i<=nump;i++){
for(int j=1;j<=nump;j++){
//当允许经过前k-1个点,i到j之间不存在经过点k的路径时
if(d[k-1][i][k]==inf || d[k-1][k][j]==inf){
d[k][i][j] = d[k-1][i][j];
}
//当允许经过前k-1个点,i到j之间不存在路径;或者存在路径,但大于加入点k后的新路径时
else if((d[k-1][i][j]==inf) || (d[k-1][i][k]+d[k-1][k][j] < d[k-1][i][j])){
d[k][i][j] = d[k-1][i][k]+d[k-1][k][j];
}
//其它情况,例如允许经过前k-1个点,存在路径且小于加入点k后的新路径
else{
d[k][i][j] = d[k-1][i][j];
}
}
}
}
给出例题:
代码:
/*floyd算法求最短路径*/
#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <string.h>
using namespace std;
#define inf 10000
int nump,nume; //路口数和路数
int d[101][101][101]; //d[k][i][j]代表从i点到j点,中间只允许加入前k个点时的最短路径长度
int main()
{
memset(d,inf,sizeof(d)); //所有路径初始化
while(scanf("%d %d",&nump,&nume)!=EOF && nump!=0 && nume!=0){
//点a到点a距离总为0
for(int i=1;i<=nump;i++){
for(int k=0;k<=nump;k++){
d[k][i][i] = 0;
}
}
//初始化两点之间距离
for(int i=1;i<=nume;i++){
int a,b,len;
scanf("%d %d %d",&a,&b,&len);
d[0][a][b] = len; d[0][b][a] = len;
}
for(int k=1;k<=nump;k++){
for(int i=1;i<=nump;i++){
for(int j=1;j<=nump;j++){
//当允许经过前k-1个点,i到j之间不存在经过点k的路径时
if(d[k-1][i][k]==inf || d[k-1][k][j]==inf){
d[k][i][j] = d[k-1][i][j];
}
//当允许经过前k-1个点,i到j之间不存在路径;或者存在路径,但大于加入点k后的新路径时
else if((d[k-1][i][j]==inf) || (d[k-1][i][k]+d[k-1][k][j] < d[k-1][i][j])){
d[k][i][j] = d[k-1][i][k]+d[k-1][k][j];
}
//其它情况,例如允许经过前k-1个点,存在路径且小于加入点k后的新路径
else{
d[k][i][j] = d[k-1][i][j];
}
}
}
}
cout<<d[nump][1][nump]<<endl;
}
return 0;
}
/*
输入示例:
3 3
1 2 5
2 3 5
3 1 2
输出:
2
*/
其次还有dijstra算法求最短路径,该算法可以求出起点到任意点的最短路径,时间复杂度为O(n^2),运算更快。基本思路如下:
- 将起点到任一点的最短路径设为d[i],初始化为无穷大;
- 利用链表来记录每个点的邻接点,以及与邻接点之间的边长;
- 初始设起点为新加入点,同时确定起点到起点的最短路径,并标记起点,(标记的目的在于,说明该点到起点的最小距离彻底确定下来了)
- 逐个搜索新加入点的邻接点,计算并判断当前邻接点到起点的最小距离,记录在dis数组中。搜索完成后,寻找当前没有被标记的点中,距离起点距离最小的点,将该点标记,并作为新的加入点;
- 循环步骤4(n-1)次,保证每个点都会在某时刻成为新加入点。
- 得到起点到任一点的最短路径。
核心代码如下:
int newp = st; //初始时,先将起点作为新加入点
//搜寻nump-1轮,能够保证每个点都有会被设为newp的时刻
for(int i=1;i<nump;i++){
//从新加入的点开始搜寻,寻找它的邻点到起点的最短距离
for(int j=0;j<e[newp].size();j++){
int next = e[newp][j].next;
int len = e[newp][j].len;
int co = e[newp][j].cost;
//dis[next]更新条件:新的dis距离更短,或者距离相同但是花费更少
if(mark[next] == true) continue;
if(dis[next]>dis[newp]+len || (dis[next]==dis[newp]+len && cost[next]>cost[newp]+co)){
dis[next] = dis[newp]+len;
cost[next] = cost[newp]+co;
}
}
//找出当前,除了已经标记确定了的dis[i]之外,其余点中到起点的距离最小的点,
//这个最短距离的点就是新的加入点,它到起点的最短距离已经确认,被mark数组标记
int minlen = inf; int minp;
for(int i=1;i<=nump;i++){
if(mark[i]==true) continue;
if(dis[i]<minlen){
minlen = dis[i];
minp = i;
}
}
mark[minp] = true;
newp = minp;
}
cout<<dis[en]<<" "<<cost[en]<<endl;
例题如下:
代码:
/*dijstra算法求最短路径*/
#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <vector>
using namespace std;
#define inf 100000
int nump,nume; //点数和边数
int mark[1001]; //已经加入点1到点i的最小路径集合中的点
int dis[1001]; //点1到点i之间的最小距离
int cost[1001]; //点1到点i之间需要的最小费用
int st,en; //起点和终点
struct edge
{
int next; //下一个点
int len; //两点连接的边长
int cost; //边的花费
};
vector<edge> e[1001];
int main()
{
//初始化标志数组和距离数组,默认dis和cost都是无穷大
for(int i=0;i<=1000;i++){
mark[i] = false;
dis[i] = inf;
cost[i] = inf;
}
while(scanf("%d %d",&nump,&nume)!=EOF && nump!=0 && nume!=0){
for(int i=1;i<=nume;i++){
e[i].clear();
}
for(int i=1;i<=nume;i++){
int a,b,d,p;
scanf("%d %d %d %d",&a,&b,&d,&p);
//无向图,点a和点b互相将对方的信息接入自己的邻点集合中
edge x;
x.next = b;x.len = d;x.cost = p;e[a].push_back(x);
x.next = a;e[b].push_back(x);
}
scanf("%d %d",&st,&en);
mark[st] = true;
dis[st] = 0;
cost[st] = 0;
int newp = st; //初始时,先将起点作为新加入点
//搜寻nump-1轮,能够保证每个点都有会被设为newp的时刻
for(int i=1;i<nump;i++){
//从新加入的点开始搜寻,寻找它的邻点到起点的最短距离
for(int j=0;j<e[newp].size();j++){
int next = e[newp][j].next;
int len = e[newp][j].len;
int co = e[newp][j].cost;
//dis[next]更新条件:新的dis距离更短,或者距离相同但是花费更少
if(mark[next] == true) continue;
if(dis[next]>dis[newp]+len || (dis[next]==dis[newp]+len && cost[next]>cost[newp]+co)){
dis[next] = dis[newp]+len;
cost[next] = cost[newp]+co;
}
}
//找出当前,除了已经标记确定了的dis[i]之外,其余点中到起点的距离最小的点,
//这个最短距离的点就是新的加入点,它到起点的最短距离已经确认,被mark数组标记
int minlen = inf; int minp;
for(int i=1;i<=nump;i++){
if(mark[i]==true) continue;
if(dis[i]<minlen){
minlen = dis[i];
minp = i;
}
}
mark[minp] = true;
newp = minp;
}
cout<<dis[en]<<" "<<cost[en]<<endl;
}
return 0;
}
/*
输入数据:
3 2
1 2 5 6
2 3 4 5
1 3
输出:
9 11
*/