最短路径
- Dijkstra
- Bellman-Ford
Dijkstra
该算法的基本思想为:
每次找到离源点最近的一个顶点,然后以该顶点为中心进行扩展最终得到源点到其余所有点的最短路径。
基本步骤如下:
- 将所有顶点分为两部分:已知最短路程的顶点集合P和未知最短路程的顶点集合Q。用book[i]表示,如果book[i]=1则表示这个顶点在集合P中,反之顶点在集合Q中。
- 设置源点s到自己的最短路径为0。其余按照实际情况进行设置。
- 在集合Q的所有定点中选择一个离源点s最近的顶点加入到集合P。并考察所有以点u为起点的边,对每一条边进行松弛操作。
- 重复第三步,如果集合Q为空,算法结束。最终dis数组中的值就是源点到所有顶点的最短路径。
#include<iostream>
using namespace std;
#define MAX 999999
int main ()
{
int book[101],e[101][101],dis[10]; //book[i]=0表示在未确定最小路径的集合中,book[i]=1表示在确定最小路径的集合中
int s,n,m;
int a,b,c;
int i,j,u;
cout << "please input the number of the city: ";
cin >> n;
cout << "is there how many roads: ";
cin >> m;
cout << "please input the start: ";
cin >> s;
for(int i =1;i<=n;++i)
{
for(int j=1;j<=n;++j)
{
if(i == j)
{
e[i][j]=0;
}
else
e[i][j]=MAX;
}
}
cout << "please input the information of the roads: ";
for(int k=1;k<=m;++k)
{
cin >> a >> b >> c;
e[a][b]=c;
}
for(i=1;i<=n;++i)
dis[i]=e[s][i];
for(i=1;i<=n;++i)
book[i]=0;
book[s]=1;
for(int k=1;k<=n-1;++k) //要去找n-1次,因为最多包含n-1次松弛 //算法的核心代码
{
for(j=1;j<=n;++j)
{
int minute=MAX;
if((book[j] ==0) && (dis[j]< minute)) //确定相邻城市的中距离最小的是哪个
{
minute = dis[j];
u=j;
}
}
book[u]=1;
for(i=1;i<=n;++i)
{
if(e[u][i]<MAX)
{
if(dis[i]>(dis[u]+e[u][i])) //将路径松弛
dis[i]=dis[u]+e[u][i];
}
}
}
for(i=1;i<=n;++i)
cout << dis[i]<<" ";
return 0;
}
Bellman-Ford及其队列优化
#include<iostream>
using namespace std;
#define MAX 999999
int main()
{
int u[101],v[101],w[101],dis[10];
int s,n,m;
int k,i;
cout << "please input the number of the city: ";
cin >> n;
cout << "is there how many roads: ";
cin >> m;
cout << "please input the start: ";
cin >> s;
cout << "please input the information of the roads: ";
for(k=1;k<=m;++k)
{
cin >> u[k] >> v[k] >> w[k];
}
for(i=1;i<=n;++i)
dis[i]=MAX;
dis[s]=0;
for(k=1;k<=n-1;++k) //进行n-1轮松弛
{
for(i=1;i<=n;++i) //枚举每一条边
{
if(dis[v[i]]>(dis[u[i]]+w[i])) //对每一条边进行松弛
dis[v[i]]=dis[u[i]]+w[i];
//dis[1][v[i]] = dis[u[i]] + w[i];
}
}
for(i=1;i<=n;++i)
cout << dis[i] << " ";
return 0;
}
上述代码中,外循环一共循环了n-1次(n为顶点的个数),内循环循环了m次(m为边的个数),即枚举每一条边。dis数组的作用与Dijkstra算法一样,是用来记录源点到其余各个顶点的最短路径。u、v合w三个数组是用来记录边的信息。
队列优化
队列优化最核心的思想就是:
每次仅对最短路径估计值发生了变化的顶点的所有出边执行松弛操作。
每次选取队首顶点u,对顶点的所有出边进行松弛操作。例如有一条u→v的边,如果通过u→v这条边是的源点到顶点v的最短路程变短,且顶点v不在当前的队列中,就将顶点v放入队尾。需要注意的是,同一个顶点同时在队列中出现多次是毫无意义的,所以我们需要一个数组来判重。在对顶点u的所有出边松弛完毕后,就将顶点u出队。
#include<iostream>
using namespace std;
#define INF 999999
int main ()
{
int dis[6],que[101]={0},book[6];
int first[10],next[10];
int u[8],v[8],w[8];
int head=1,tail=1;
int s,n,m,i;
cout << "please input the number of the city: ";
cin >> n;
cout << "is there how many roads: ";
cin >> m;
cout << "please input the start: ";
cin >> s;
cout << "please input the information of the roads: ";
for(i=1;i<=n;++i)
dis[i]=INF;
dis[s]=0;
for(i=1;i<=n;++i)
book[i]=0;
for(i=1;i<=n;++i)
first[i]=-1;
for(i=1;i<=m;++i)
{
cin >> u[i] >> v[i] >> w[i];
next[i]=first[u[i]];
first[u[i]]=i;
}
que[tail]=s;
++tail;
book[s]=1;
while(head < tail)
{
int k;
k=first[que[head]];
while(k != -1)
{
if(dis[v[k]]>(dis[u[k]]+w[k]))
{
dis[v[k]]=dis[u[k]]+w[k];
if(book[v[k]] == 0)
{
que[tail]=v[k];
++tail;
book[v[k]]=1;
}
}
k=next[k];
}
book[que[head]]=0;
++head;
}
for(i=1;i<=n;++i)
cout << dis[i] << " ";
return 0;
}