关于最短路径算法输出沿途路径的结点
第一次碰到这个问题是在这题:PAT甲级1030
一道最基础的最短路算法,但是一开始始终是内存和时间超限,原因是path数组没有赋值好,导致最后Vector无穷加原因,所以内存和时间超限。。错误代码如下:(原因也在代码中注释说明)
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std ;
const int Inf = 99999999 ;
int G[510][510],pay[510][510],path[510],cost[510],dist[510],visit[510];
int main(){
fill(G[0],G[0]+510*510,Inf) ;
fill(pay[0],pay[0]+510*510,Inf) ;
int N,M,start,destination ;
cin >> N >> M >> start >> destination ;
int v,w,d,c ;
while(M--){
scanf("%d %d %d %d",&v,&w,&d,&c) ;
G[v][w] = d ;
G[w][v] = d ;
pay[v][w]= c ;
pay[w][v] = c ;
}
for(int i = 0;i<N;i++){
dist[i] = G[start][i] ; //注意这里给源结点的邻接顶点的距离都赋值好了,这是问题的开始
cost[i] = pay[start][i];
visit[i] = 0 ;
}
dist[start] = 0 ;
cost[start] = 0 ;
path[start] = -1 ; //这里给源结点path设成-1没问题
for(int node = 0;node<N;node++){
int mincity = -1,mindist = Inf ;
for(int i=0;i<N;i++)
if(dist[i] < mindist && !visit[i]){
mincity = i ;
mindist = dist[i] ;
}
if(mincity == -1)
break ;
visit[mincity] = 1 ;
for(int i = 0;i<N;i++){
if(G[mincity][i] != Inf && visit[i] == 0){
/*问题就在这第一步,一开始mincity是源结点即start,于是开始更新start的邻接顶点的dist
、cost和path,但是注意前面在为dist初始化时已经给start的邻接顶点的dist更新好了,
也就是说下面这个if判断if(dist[i] > G[mincity][i] + dist[mincity])对于
start来说始终不会执行,但是很蠢的一点是我前面并没有给start的邻接顶点的path更新好
这样一来start的邻接顶点的path始终是随机值(前面也没有给path赋初值,以为path会自动更新)
*/
if(dist[i] > G[mincity][i] + dist[mincity]){
dist[i] = G[mincity][i] + dist[mincity] ;
cost[i] = pay[mincity][i] +cost[mincity];
path[i] = mincity ;
}
else if(dist[i] == G[mincity][i] + dist[mincity]){
if(cost[i] > pay[mincity][i] + cost[mincity]){
cost[i] = pay[mincity][i] + cost[mincity];
path[i] = mincity ;
}
}
}
}
}
vector<int> a ;
int p = destination ;
while(path[p] != -1){
a.push_back(p) ;
p = path[p] ;
}
a.push_back(p) ;
for(int i = a.size()-1;i>=0;i--)
cout << a[i] << " " ;
cout << dist[destination] << " "<<cost[destination] ;
return 0 ;
}
解决方案
有两种解决办法:
1、在错误代码的基础上,将path和dist一样,在前面赋初值时将start的邻接点的path更新好。
2、在错误代码的基础上,将dist和path一样,前面初始化时不将start的邻接点的dist更新(即dist数组除了dist[start] = -1外,其余dist[i] 均等于Inf(无穷大)。。)
方案一代码:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std ;
const int Inf = 99999999 ;
int G[510][510],pay[510][510],path[510],cost[510],dist[510],visit[510];
int main(){
fill(G[0],G[0]+510*510,Inf) ;
fill(pay[0],pay[0]+510*510,Inf) ;
int N,M,start,destination ;
cin >> N >> M >> start >> destination ;
int v,w,d,c ;
while(M--){
scanf("%d %d %d %d",&v,&w,&d,&c) ;
G[v][w] = d ;
G[w][v] = d ;
pay[v][w]= c ;
pay[w][v] = c ;
}
for(int i = 0;i<N;i++){
dist[i] = G[start][i] ;
cost[i] = pay[start][i];
visit[i] = 0 ;
if(G[start][i] != Inf)
path[i] = start ; //解决的代码在这里,在这里将start的邻接点的path都已更新好
}
dist[start] = 0 ;
cost[start] = 0 ;
path[start] = -1 ;
for(int node = 0;node<N;node++){
int mincity = -1,mindist = Inf ;
for(int i=0;i<N;i++)
if(dist[i] < mindist && !visit[i]){
mincity = i ;
mindist = dist[i] ;
}
if(mincity == -1)
break ;
visit[mincity] = 1 ;
for(int i = 0;i<N;i++){
if(G[mincity][i] != Inf && visit[i] == 0){
if(dist[i] > G[mincity][i] + dist[mincity]){
dist[i] = G[mincity][i] + dist[mincity] ;
cost[i] = pay[mincity][i] +cost[mincity];
path[i] = mincity ;
}
else if(dist[i] == G[mincity][i] + dist[mincity]){
if(cost[i] > pay[mincity][i] + cost[mincity]){
cost[i] = pay[mincity][i] + cost[mincity];
path[i] = mincity ;
}
}
}
}
}
vector<int> a ;
int p = destination ;
while(path[p] != -1){
a.push_back(p) ;
p = path[p] ;
}
a.push_back(p) ;
for(int i = a.size()-1;i>=0;i--)
cout << a[i] << " " ;
cout << dist[destination] << " "<<cost[destination] ;
return 0 ;
}
方案二代码:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std ;
const int Inf = 99999999 ;
int G[510][510],pay[510][510],path[510],cost[510],dist[510],visit[510];
int main(){
fill(G[0],G[0]+510*510,Inf) ;
fill(pay[0],pay[0]+510*510,Inf) ;
int N,M,start,destination ;
cin >> N >> M >> start >> destination ;
int v,w,d,c ;
while(M--){
scanf("%d %d %d %d",&v,&w,&d,&c) ;
G[v][w] = d ;
G[w][v] = d ;
pay[v][w]= c ;
pay[w][v] = c ;
}
for(int i = 0;i<N;i++){
dist[i] = Inf ; //改动的地方在这里,将所以dist都赋值为Inf,这样下面那个if判断才能进去
cost[i] = pay[start][i];
visit[i] = 0 ;
}
dist[start] = 0 ; //这里start的dist当然还是0了
cost[start] = 0 ;
path[start] = -1 ;
for(int node = 0;node<N;node++){
int mincity = -1,mindist = Inf ;
for(int i=0;i<N;i++)
if(dist[i] < mindist && !visit[i]){
mincity = i ;
mindist = dist[i] ;
}
if(mincity == -1)
break ;
visit[mincity] = 1 ;
for(int i = 0;i<N;i++){
if(G[mincity][i] != Inf && visit[i] == 0){
if(dist[i] > G[mincity][i] + dist[mincity]){//这个if一开始会去更新start结点的dist和path、cost
dist[i] = G[mincity][i] + dist[mincity] ;
cost[i] = pay[mincity][i] +cost[mincity];
path[i] = mincity ;
}
else if(dist[i] == G[mincity][i] + dist[mincity]){
if(cost[i] > pay[mincity][i] + cost[mincity]){
cost[i] = pay[mincity][i] + cost[mincity];
path[i] = mincity ;
}
}
}
}
}
vector<int> a ;
int p = destination ;
while(path[p] != -1){
a.push_back(p) ;
p = path[p] ;
}
a.push_back(p) ;
for(int i = a.size()-1;i>=0;i--)
cout << a[i] << " " ;
cout << dist[destination] << " "<<cost[destination] ;
return 0 ;
}
当然类似的问题又很多(BFS算法也会出现类似问题),重要的是搞清楚哪些地方自己已解决,哪些地方是要去更新或者说遍历,思路一定要清晰,这样才不会出错。。。