最短路问题
Floyd(有向图)
- 首先是需要构造邻接矩阵,利用二维数组构造,每一行代表一个起始点,每一列代表一个终点,数组中的值代表从起点到终点的距离(即权重),起终点相同即权重为0,两点之间没有通路则权重为∞。
- 现在邻接矩阵中存的就是,从起点直接到终点的距离,但有时候可能两个点之间的距离并不是最短,所以我们还需要一个或者多个中间点k。例如从点3走到点2的距离是无穷大,但从点3经点1再到点2的距离为9。
- 算法过程大致如下,
- 先假设我们的中间点为k,当k=1时,代表途中经过的中间点为1,然后开始遍历邻接矩阵。假设此时遍历到第三行第二列,则为以3为起点2为终点且经过点1的距离,式子为
map[3][1]+map[3][2]
将这一计算结果与原本map[3][2]
的值进行比较,取较小值更新。
经过一个或不经过中间点时,两点之间的最小距离
当邻接链表以k=1被遍历完一遍之后,邻接链表的内容就已经变为经过一个或不经过中间点时,两点之间的最小距离,此时进行k=2的遍历。完成之后邻接链表的内容变为经过一个、两个或不经过中间点时,两点之间的最小距离。以此类推。。。
###Floyd方法核心代码
void floyd() { for(int k = 0; k < n; k++) for(int i = 0; i < n; i++) for(int j = 0; j < n; j++) map[i][j] = min(map[i][j], map[i][k] + map[k][j]); }
例题
- 我们以杭电|畅通工程续为例
- 示例代码:
#include<algorithm>
#include<iostream>
#include<cstring>
#define inf 0x3f3f3f3f //代表无穷大using namespace std;
int mapp[205][205];
int n,m;
void floyd(){
for(int k=0;k<n;k++){
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
mapp[i][j]=min(mapp[i][j],mapp[i][k]+mapp[k][j]);
}
}
}
}
void init(){
for(int i=0;i<204;i++){
for(int j=0;j<204;j++){
if(i==j){
mapp[i][j]=0;
}
else{
mapp[i][j]=inf;
}
}
}
}
int main(){
while(~scanf("%d %d",&n,&m)){
init();
int a,b,x,s,t;
for(int i=0;i<m;i++){
scanf("%d %d %d",&a,&b,&x);
if(x<mapp[a][b]){
mapp[a][b]=x;
mapp[b][a]=x;
}
}
floyd();
scanf("%d %d",&s,&t);
if(mapp[s][t]==inf){
printf("-1\n");
continue;
}
printf("%d\n",mapp[s][t]);
}
}
Djkstar(有单另的答案矩阵)
- 首先也是先构建邻接矩阵,不过这个邻接矩阵为双向。即mapi == mapj;
- 与Floyd不同的是,Djkstar还需要一个单另的一维答案矩阵,更新时在答案矩阵上进行更新。
- 算法过程大致如下,遍历找到距离当前点最近的点p,将 从起点走到当前点的最短距离再加上从当前点到p点的距离 与答案矩阵中从起点直接到的距离相比较,更新成较小的那个。这样遍历所有的点,最后答案矩阵中相应点下的值就是从起点到相应点的最短路径。注:Djkstar方法还需要一个一位数组来记录哪些点已经被遍历过。
具体图解过程如下:
上图是最开始的图、答案矩阵、和邻接矩阵
- 此时假设1为起点,则把邻接矩阵第一横行拷贝进答案矩阵
此时1为当前点
- 很显然距离点1最近的点为点2,此时走过的距离为7,这时候从点2走到点4的距离是15+722;小于正无穷,所以更新为22;且点2走到点3的距离是10+717;大于9,所以不更新。
此时2为当前点
- 此时距离2最近的点是点3,所以来到点3;此时答案矩阵中点3对应的距离是9;所以从点3到点4的距离为9+1120,小于22,所以点4更新为20.从点3到点6的距离为9+211,小于14,所以点6更新为14.
此时3为当前点
- 如此这般下去,直到遍历完所有的点。
###Djkstar方法核心代码
//pos为当前点,minn为离当前点最近的点
//mark[]是记录所有点是否被遍历过的数组
//ans[]是答案矩阵
//mapp[]是邻接矩阵
for(int i=1;i<n;i++){
int pos,minn=inf;
for(int j=0;j<n;j++){
if(mark[j]==0 && ans[j]<minn){//找没有走过的点,并且找距离起始点最短的路
pos=j;
minn=ans[j];
}
}
mark[pos]=1;
for(int j=0;j<n;j++){
if(ans[j]>ans[pos]+mapp[pos][j]){
ans[j]=ans[pos]+mapp[pos][j];
}
}
}
例题
- 我们依然以杭电|畅通工程续为例
- 示例代码:
#include<stdio.h>
#define inf 0x3f3f3f3f
int map[205][205];
int ans[205];
int mark[205];
int main()
{
int n,m;
int a,b,x;
int s,e;
while(~scanf("%d %d",&n,&m))
{
for(int i = 0;i < 205;i++)
{
for(int j = 0;j < 205;j++)
{
map[i][j] = inf;
}
}
for(int i = 0;i < 205;i++)
{
map[i][i] = 0;
mark[i] = 1;
}
//地图初始化
for(int i = 0;i < m;i++)
{
scanf("%d %d %d",&a,&b,&x);
if(x < map[a][b]){
map[a][b] = x;
map[b][a] = x;
}
}
scanf("%d %d",&s,&e);
//接收数据
for(int i = 0;i < n;i++)
{
ans[i] = map[s][i];
}
//答案矩阵初始化
mark[s] = 0;
for(int i = 0;i < n;i++)
{
int pos,minn = inf;
for(int j = 0;j < n;j++)
{
if(mark[j] == 1 && ans[j] < minn)
{
pos = j;
minn = ans[j];
}
}
mark[pos] = 0;
for(int j = 0;j < n;j++)
{
if(ans[j] > ans[pos] + map[pos][j])
ans[j] = ans[pos] + map[pos][j];
}
}
if(ans[e] == inf)
printf("-1\n");
else
printf("%d\n",ans[e]);
}
return 0;
}