最短路径是图论中一个很经典的问题:给定图G(V,E),求一条从起点到终点的路径,使的这条路径上经过的所有边的边权之和最小。
1.Dijkstra算法
Dijkstra算法(迪杰斯特拉)用来解决单源最短路径问题,即给定图G和起点s,通过算法得到s到达其他顶点的最短距离。Dijkstra的基本思想是对图G(V,E)设置集合S,存放已被访问的顶点,然后每次从集合V-S中选择与起点s的最短距离最小的一个顶点(记为u),访问并加入集合S,之后,令顶点u为中介点,优化起点s与所有从u能到达的顶点v之间的最短距离,这样的操作执行n次(n为顶点个数),直到集合S已包含所有顶点。
Dijsktra算法的策略是:
设置集合S存放已被访问的顶点(即已攻占的城市),然后执行n次下面的两个步骤(n为顶点个数):
(1)每次从集合V-S(即未攻占的城市)中选择与起点s的最短距离最小的一个顶点(记为u),访问并加入集合S(即令其已被攻占)。
(2)之后,令顶点u为中介点,优化起点s与所有从u能到达的顶点v之间的最短距离。
Dijkstra算法的具体实现:
(1)集合S可以用一个bool数组vis[]来实现,即当vis[i]==true时表示顶点Vi已被访问,当vis[i]==false时表示顶点Vi未被访问。
(2)令int型数组d[]表示起点s到达顶点Vi的最短距离,初始时除了起点s的d[s]赋为0,其余顶点都赋为一个很大的数,来表示inf,即不可达。
伪代码如下:
//G为图,一般设为全局变量:数组d为源点到达各点的最短路径长度,s为起点
Dijkstra(G, d[], s){
初始化;
for(循环n次){
u = 使d[u]最小的还未被访问的顶点的标号;
记u已被访问;
for(从u出发能到达的所有顶点v){
if(v未被访问 && 以u为中介点使s到顶点v的最短距离d[v]更优{
优化d[v];
)
}
}
}
邻接矩阵版Dijkstra算法:
constint MAXV=1000; //最大顶点数
constint INF=1000000000; //设INF为一个很大的数
int n, G[MAXV][MAXV]; //设n为顶点数,MAXV为最大顶点数
int d[MAXV] ; //起点到达各点的最短路径长度
bool vis[MAXV] = {false}; //标记数组,初值为false,表示未被访问
void Dijkstra(int s){// s为起点
fill(d, d+MAXV, INF); //fill函数将整个d数组赋为inf
d[s] = 0; //起点s到自身的距离为0
for(int =0; i<n; i++){ //循环n次
int u=-1, MIN = INF; //u使d[u]最小,MIN存放该最小的d[u]
for(int j=0; j<n; j++){ //找到未访问的顶点中d[]的最小
if(vis[j] == false && d[j]<MIN){
MIN = d[j];
u = j;
}
}
//找不到小于INF的d[u],说明剩下的顶点和起点s不连通
if(u == -1) return;
vis[u] = true; //标记u已访问
for(int v=0; v<n; v++){
//如果v未访问 && u能到达v &&以u为中介点可以使d[v]更优
if(vis[v] == false && G[u][v] != INF&& d[u]+G[u][v]<d[v]){
d[v] = d[u] + G[u][v]; //优化d[v]
}
}
}
}
邻接表版Dijkstra算法:
vector<Node> Adj[MAXV]; //Adj[u]存放从顶点u出发可以到达的所有顶点
int n;
int d[MAXV];
bool vis[MAXV] ={false};
void Dijkstra(int s){ //s为起点
fill(d, d+MAXV, INF);
d[s] = 0;
for(int i=0; i<n; i++){
int u =-1, MIN = INF;
for(int j=0; j<n; j++){
if(vis[j] == false && d[j] < MIN){
u = j;
MIN = d[j];
}
}
if(u == -1) return;
vis[u] = true;
for(int j=0; j<Adj[u].size; j++){
int v = Adj[u][j].v;
if(vis[v] == false && d[u]+Adj[u][j].dis < d[v] ){
d[v] = d[u] + Adj[u][j].dis; //优化
}
}
}
}
2.Floyd算法
Floyd算法用来解决全源最短路径,即对给定的图G(V,E),求任意两点u,v之间的最短路径长度,时间复杂度O(n3),由于n3的复杂度,由于n3的复杂度决定了顶点数n的限制约在200以内,因此使用邻接矩阵Floyd算法是非常合适且方便的。
Floyd算法基于这样一个事实:如果存在顶点k,使得以k作为中介点时顶点i和顶点j的当前最短距离缩短,则使用顶点k作为顶点i和顶点j的中介点,即当dis[i][k] + dis[k][j] < dis[i][j]时,令dis[i][j] = dis[i][k] + dis[k][j] (其中dis[i][j]表示从顶点i到顶点j的最短距离)。Floyd算法的流程如下:
枚举顶点1<= k <=n
以顶点k作为中介点,枚举所有顶点对i和j
如果dis[i][k] + dis[k][j] <dis[i][j]成立
赋值dis[i][j] = dis[i][k] + dis[k][j]
Floyd算法代码如下:
#include<cstdio>
#include<algorithm>
using namespace std;
const int INF = 1000000000;
const int MAXV = 200; //MAXV为最大顶点数 //MAXV为最大顶点数
const int n , m; // n为顶点数,m为边数
int dis[MAXV][MAXV]; //dis[i][j]表示顶点i和顶点j的最短距离
void Floyd(){
for(int k =0; k<n; k++){
for(int i=0; i<n; i++){
for(int j=0; j<n; j++){
if(dis[i][k]!=INF && dis[k][j]!=INF && dis[i][k] + dis[k][j] < dis[i][j]){
dis[i][j] = dis[i][k] + dis[k][j]; //优化路径
}
}
}
}
}
int main(){
int u, v, w;
fill(dis[0], dis[0]+ MAXV*MAXV, INF); //dis 赋处值
sacnf("%d%d", &n, &m); //顶点数n、边数m
for(int i=0; i<m; i++){
dis[i][i] = 0; //顶点i到顶点i的距离初始化为0
}
for(int i=0; i<n; i++){
scanf("%d%d%d", &u, &v, &w);
dis[u][v] = w; //以有向图为例进行输入
}
Floyd(); //算法入口
for(int i =0; i<n; i++){
for(int j=0; j<n; j++){
printf("%d ", dis[i][j]);
}
printf("\n")
}
return 0;
}