引子:我们在求解最短路的时候,有的时候需要输出路径,这里就介绍下路径记录及输出的一些方法。
为了更好理解,提出一个问题:
给出n个车站,m条单向路线以及 每条路线的信息即 起点终点和对应的花费(n与m同时为0表示结束),然后给出一系列的起点s和终点e(s和e同时为-1表示结束),让你求出最小花费并输出路径(假定给出的两点均是可达的)。
(一) dijkstra算法实现:
思路:用一个数组path[]记录前驱顶点,找到最短路后,从终点倒向追踪,直到找到起点为止。为了方便我这里用了栈来记录,不用栈用一个新的数组记录也可以的。
需要注意的有一点:在初始化path[]数组时,在i(1 <= i <= n)不等于起点s的前提下,对那些可以由s直达的点i,要有path[i] = s。
代码就是这样:pre[i] = dist[i]!=INF&&i!=s ? s : -1;
代码实现:
#include <cstdio>
#include <cstring>
#include <stack>
#include <algorithm>
#define INF 1000000+10
using namespace std;
int Map[500][500];
int pre[500];//记录当前顶点的 前一个顶点
int dist[500];
bool vis[500];
int n, m;
void init()
{
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= n; j++)
{
Map[i][j] = i==j ? 0 : INF;
}
}
}
void getMap()
{
int a, b, c;
while(m--)
{
scanf("%d%d%d", &a, &b, &c);
if(Map[a][b] > c)
Map[a][b] = c;
}
}
void dijkstra(int s, int e)
{
int Min, next;
for(int i = 1; i <= n; i++)
{
dist[i] = Map[s][i];
vis[i] = false;
pre[i] = dist[i]!=INF&&i!=s ? s : -1;//初始化要注意
}
vis[s] = true;
for(int i = 2; i <= n; i++)
{
Min = INF;
for(int j = 1; j <= n; j++)
{
if(!vis[j] && dist[j] < Min)
{
Min = dist[j];
next = j;
}
}
vis[next] = true;
for(int j = 1; j <= n; j++)
{
if(!vis[j] && dist[j] > dist[next] + Map[next][j])
{
dist[j] = dist[next] + Map[next][j];
pre[j] = next;//记录
}
}
}
}
int main()
{
int s, e;
while(scanf("%d %d", &n, &m), n||m)
{
init();
getMap();
while(scanf("%d%d", &s, &e), s!=-1||e!=-1)
{
dijkstra(s, e);
if(s == e)
{
printf("从%d到%d的最优路线 : %d\n", s, e, s);
printf("最小花费 : %d\n", 0);
continue;
}
int now = pre[e];
stack<int> path;
path.push(e);
while(1)
{
path.push(now);
if(now == s)
break;
now = pre[now];
}
printf("从%d到%d的最优路线 : %d", s, e, s);
while(!path.empty())
{
printf("-->%d", path.top());
path.pop();
}
printf("\n");
printf("最小花费 : %d\n", dist[e]);
}
}
return 0;
}
(二) floyd算法实现:
思路:用一个二维数组path[][]来记录。这里有两种方法,1 用path[ i ][ j ]记录 j 的前驱顶点 ;2 用path[ i ][ j ]记录 i 的后面的点。
提醒:需要注意的是path的初始化
代码实现第一种方法:
#include <cstdio>
#include <cstring>
#include <stack>
#include <algorithm>
#define INF 1000000+10
using namespace std;
int Map[500][500];
int pre[500][500];//记录当前顶点的 前一个顶点
int n, m;
void init()
{
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= n; j++)
{
Map[i][j] = i==j ? 0 : INF;
pre[i][j] = i;//初始化
}
}
}
void getMap()
{
int a, b, c;
while(m--)
{
scanf("%d%d%d", &a, &b, &c);
if(Map[a][b] > c)
Map[a][b] = c;
}
}
void floyd()
{
int i, j, k;
for(k = 1; k <= n; k++)
{
for(i = 1; i <= n; i++)
{
for(j = 1; j <= n; j++)
{
if(Map[i][j] > Map[i][k] + Map[k][j])
{
Map[i][j] = Map[i][k] + Map[k][j];
pre[i][j] = pre[k][j];//记录
}
}
}
}
}
int main()
{
int s, e;
while(scanf("%d %d", &n, &m), n||m)
{
init();
getMap();
floyd();
while(scanf("%d%d", &s, &e), s!=-1||e!=-1)
{
if(s == e)
{
printf("从%d到%d的最优路线 : %d\n", s, e, s);
printf("最小花费 : %d\n", 0);
continue;
}
int now = pre[s][e];
stack<int> path;//记录路径
path.push(e);
while(1)
{
path.push(now);
if(now == s)
break;
now = pre[s][now];
}
printf("从%d到%d的最优路线 : %d", s, e, s);
path.pop();
while(!path.empty())
{
printf("-->%d", path.top());
path.pop();
}
printf("\n");
printf("最小花费 : %d\n", Map[s][e]);
}
}
return 0;
}
代码实现第二种方法:这个没有用栈 建议掌握好这个方法
#include <cstdio>
#include <cstring>
#include <stack>
#include <algorithm>
#define INF 1000000+10
using namespace std;
int Map[500][500];
int pre[500][500];//记录当前顶点的 前一个顶点
int n, m;
void init()
{
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= n; j++)
{
Map[i][j] = i==j ? 0 : INF;
pre[i][j] = j;//初始化
}
}
}
void getMap()
{
int a, b, c;
while(m--)
{
scanf("%d%d%d", &a, &b, &c);
if(Map[a][b] > c)
Map[a][b] = c;
}
}
void floyd()
{
int i, j, k;
for(k = 1; k <= n; k++)
{
for(i = 1; i <= n; i++)
{
for(j = 1; j <= n; j++)
{
if(Map[i][j] > Map[i][k] + Map[k][j])
{
Map[i][j] = Map[i][k] + Map[k][j];
pre[i][j] = pre[i][k];//记录
}
}
}
}
}
int main()
{
int s, e;
while(scanf("%d %d", &n, &m), n||m)
{
init();
getMap();
floyd();
while(scanf("%d%d", &s, &e), s!=-1||e!=-1)
{
if(s == e)
{
printf("从%d到%d的最优路线 : %d\n", s, e, s);
printf("最小花费 : %d\n", 0);
continue;
}
printf("从%d到%d的最优路线 : %d", s, e, s);
int now = pre[s][e];
while(1)
{
printf("-->%d", now);
if(now == e)
break;
now = pre[now][e];
}
printf("\n");
printf("最小花费 : %d\n", Map[s][e]);
}
}
return 0;
}
(三)SPFA算法实现:
思路:同dijkstra。。。详看代码
代码实现:
#include <cstdio>
#include <cstring>
#include <queue>
#include <stack>
#include <algorithm>
#define INF 1000000+10
using namespace std;
struct Edge
{
int from, to, val, next;
}edge[250000];
int dist[500];
int head[500], top;
bool vis[500];
int pre[500];//记录前驱顶点
int n, m;
void init()
{
top = 0;
memset(head, -1, sizeof(head));
memset(pre, -1, sizeof(pre));
}
void addEdge(int u, int v, int w)
{
Edge E = {u, v, w, head[u]};
edge[top] = E;
head[u] = top++;
}
void getMap()
{
int a, b, c;
while(m--)
{
scanf("%d%d%d", &a, &b, &c);
addEdge(a, b, c);
}
}
void SPFA(int s, int e)
{
queue<int> Q;
for(int i = 1; i <= n; i++)
dist[i] = i==s ? 0 : INF;
memset(vis, false, sizeof(vis));
vis[s] = true;
Q.push(s);
while(!Q.empty())
{
int u = Q.front();
Q.pop();
vis[u] = false;
for(int i = head[u]; i != -1; i = edge[i].next)
{
Edge E = edge[i];
if(dist[E.to] > dist[u] + E.val)
{
dist[E.to] = dist[u] + E.val;
pre[E.to] = u;
if(!vis[E.to])
{
vis[E.to] = true;
Q.push(E.to);
}
}
}
}
}
int main()
{
int s, e;
while(scanf("%d%d", &n, &m), n||m)
{
init();
getMap();
while(scanf("%d%d", &s, &e), s!=-1||e!=-1)
{
SPFA(s, e);
if(s == e)
{
printf("从%d到%d最优路线 : %d\n", s, e, s);
printf("最小花费 : %d\n", 0);
continue;
}
stack<int> path;
path.push(e);
int now = pre[e];
while(1)
{
path.push(now);
if(now == s)
break;
now = pre[now];
}
printf("从%d到%d最优路线 : %d", s, e, s);
path.pop();
while(!path.empty())
{
printf("-->%d", path.top());
path.pop();
}
printf("\n");
printf("最小花费 : %d\n", dist[e]);
}
}
return 0;
}