从任意节点i到任意节点j的最短路径不外乎2种可能:
(1)是直接从i到j,
(2)是从i经过若干个节点k到j。
因此,我们假设dis(i,j)为节点u到节点v的最短路径的距离,对于每一个节点k,我们检查dis(i,k) + dis(k, j) < dis(i,j)是否成立,如果成立,证明从i到k再到j的路径比i直接到j的路径短,我们便设置dis(i,j) = dis(i,k) + dis(k,j),这样一来,当我们遍历完所有节点k,dis(i,j)中记录的便是i到j的最短路径的距离。
解析
给定一个n个点m条边的有向图,图中可能存在重边和自环,边权可能为负数。
再给定k个询问,每个询问包含两个整数x和y,表示查询从点x到点y的最短距离,如果路径不存在,则输出“impossible”。
数据保证图中不存在负权回路。
代码:
#include<bits/stdc+.h>
using namespace std;
const int N=210,INF=1e9;
int n,m,k,d[N][N];
void floyd(){
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
}
int main(void){
cin>>n>>m>>k;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++){
if(i==j)d[i][j]=0;
else d[i][j]=INF;
}
for(int i=0,a,b,c;i<m;i++){
cin>>a>>b>>c;
d[a][b]=min(d[a][b],c);
}
floyd();
for(int i=0,x,y;i<k;i++){
cin>>x>>y;
if(d[x][y]>INF/2)cout<<"impossible"<<endl;
else cout<<d[x][y]<<endl;
}
return 0;
}
答:通过Dijkstra计算图G中的最短路径时,需要指定起点s(即从顶点s开始计算)。
此外,引进两个集合S和U。S的作用是记录已求出最短路径的顶点(以及相应的最短路径长度),而U则是记录还未求出最短路径的顶点(以及该顶点到起点s的距离)。
初始时,S中只有起点s;U中是除s之外的顶点,并且U中顶点的路径是”起点s到该顶点的路径”。然后,从U中找出路径最短的顶点,并将其加入到S中;接着,更新U中的顶点和顶点对应的路径。 然后,再从U中找出路径最短的顶点,并将其加入到S中;接着,更新U中的顶点和顶点对应的路径。 … 重复该操作,直到遍历完所有顶点。
初始时,S只包含起点s;U包含除s外的其他顶点,且U中顶点的距离为”起点s到该顶点的距离”[例如,U中顶点v的距离为(s,v)的长度,然后s和v不相邻,则v的距离为∞]。
从U中选出”距离最短的顶点k”,并将顶点k加入到S中;同时,从U中移除顶点k。
更新U中各个顶点到起点s的距离。之所以更新U中顶点的距离,是由于上一步中确定了k是求出最短路径的顶点,从而可以利用k来更新其它顶点的距离;例如,(s,v)的距离可能大于(s,k)+(k,v)的距离。