【题目链接】
【题目考点】
1. 图论 最短路径
图中有V个顶点E条边,最短路径算法时间复杂度:
- 朴素Dijkstra算法: O ( V 2 ) O(V^2) O(V2)
- Dijkstra堆优化算法: O ( E l o g E ) O(ElogE) O(ElogE)
- SPFA算法:一般情况下 O ( k E ) O(kE) O(kE),k为顶点平均入队次数,最坏情况下 O ( V E ) O(VE) O(VE)
- Floyd算法: O ( V 3 ) O(V^3) O(V3)
空间复杂度:
- 邻接矩阵: O ( V 2 ) O(V^2) O(V2)
- 邻接表: O ( V + E ) O(V+E) O(V+E)
【解题思路】
该题输入各顶点坐标,以及每条边是哪两个点相连。那么需要我们手动计算两点间距离,作为这条边的权值。使用两点间距离公式:
(
x
1
,
x
2
)
(x_1,x_2)
(x1,x2)到
(
y
1
,
y
2
)
(y_1,y_2)
(y1,y2)的距离
(
x
1
−
x
2
)
2
+
(
y
1
−
y
2
)
2
\sqrt{(x_1-x_2)^2+(y_1-y_2)^2}
(x1−x2)2+(y1−y2)2
建图完成后,就是一个标准的求最短路径问题。
注意:表示距离的数组,边的权值,在该问题中都是double类型的。
double类型的数组dis,将元素都设为大约为
1.08
∗
1
0
16
1.08*10^{16}
1.08∗1016的较大值,可以写memset(dis, 0x43, sizeof(dis));
该题顶点数目为100,那么边最多10000个。用哪种算法及存储结构都可以。
该题顶点个数较少,可以使用Floyd算法及邻接矩阵。本题主要展示最短路径算法使用邻接矩阵时的写法。
如想看邻接表的写法,可以参考我在同一章节下其他题目的题解。
【题解代码】
解法1:Floyd算法+邻接矩阵
#include<bits/stdc++.h>
using namespace std;
#define N 105
struct Cord
{
int x, y;
};
double edge[N][N];
int n, m, st, te;//st:起点 te:终点
double dis[N][N];//dis[i][j]:顶点i到顶点j的最短距离
Cord ver[N];//保存每个顶点的坐标
double getDis(Cord a, Cord b)//获取点a到点b两点间直线距离
{
return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
void init()
{
int f, t;
cin >> n;
for(int i = 1; i <= n; ++i)
cin >> ver[i].x >> ver[i].y;
cin >> m;
for(int i = 1; i <= m; ++i)
{
cin >> f >> t;
edge[f][t] = edge[t][f] = getDis(ver[f], ver[t]);//顶点f到顶点t的边的权值为点f到点t的直线距离
}
cin >> st >> te;
}
void floyd()
{
memset(dis, 0x43, sizeof(dis));//这样可以使dis中每个元素的值大约为1.08e16,是一个较大值
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= n; ++j)
if(edge[i][j] > 0)
dis[i][j] = edge[i][j];
for(int k = 1; k <= n; ++k)
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= n; ++j)
if(dis[i][j] > dis[i][k] + dis[k][j])
dis[i][j] = dis[i][k] + dis[k][j];
}
int main()
{
init();
floyd();
cout << fixed << setprecision(2) << dis[st][te];
return 0;
}
解法2:朴素Dijkstra算法+邻接矩阵
#include<bits/stdc++.h>
using namespace std;
#define N 105
struct Cord
{
int x, y;
};
double edge[N][N];
int n, m, st, te;//st:起点 te:终点
double dis[N];//dis[i]:st到顶点i的最短距离
bool vis[N];
Cord ver[N];//保存每个顶点的坐标
double getDis(Cord a, Cord b)//获取点a到点b两点间直线距离
{
return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
void init()
{
int f, t;
cin >> n;
for(int i = 1; i <= n; ++i)
cin >> ver[i].x >> ver[i].y;
cin >> m;
for(int i = 1; i <= m; ++i)
{
cin >> f >> t;
edge[f][t] = edge[t][f] = getDis(ver[f], ver[t]);//顶点f到顶点t的边的权值为点f到点t的直线距离
}
cin >> st >> te;
}
void dijkstra()
{
memset(dis, 0x43, sizeof(dis));//这样可以使dis中每个元素的值大约为1.08e16,是一个较大值
dis[st] = 0;
for(int k = 1; k <= n; ++k)
{
int u = 0;
for(int i = 1; i <= n; ++i)
if(vis[i] == false && (u == 0 || dis[i] < dis[u]))
u = i;
vis[u] = true;
for(int v = 1; v <= n; ++v)
{
if(edge[u][v])
{
double w = edge[u][v];
if(vis[v] == false && dis[v] > dis[u] + w)
dis[v] = dis[u] + w;
}
}
}
}
int main()
{
init();
dijkstra();
cout << fixed << setprecision(2) << dis[te];
return 0;
}
解法3:SPFA算法+邻接矩阵
#include<bits/stdc++.h>
using namespace std;
#define N 105
struct Cord
{
int x, y;
};
double edge[N][N];
int n, m, st, te;//st:起点 te:终点
double dis[N];//dis[i]:st到顶点i的最短距离
bool vis[N]; //vis[i]:i是否在队列内
Cord ver[N];//保存每个顶点的坐标
double getDis(Cord a, Cord b)//获取点a到点b两点间直线距离
{
return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
void init()
{
int f, t;
cin >> n;
for(int i = 1; i <= n; ++i)
cin >> ver[i].x >> ver[i].y;
cin >> m;
for(int i = 1; i <= m; ++i)
{
cin >> f >> t;
edge[f][t] = edge[t][f] = getDis(ver[f], ver[t]);//顶点f到顶点t的边的权值为点f到点t的直线距离
}
cin >> st >> te;
}
void spfa()
{
memset(dis, 0x43, sizeof(dis));//这样可以使dis中每个元素的值大约为1.08e16,是一个较大值
queue<int> que;
dis[st] = 0;
que.push(st);
vis[st] = true;
while(que.empty() == false)
{
int u = que.front();
que.pop();
vis[u] = false;
for(int v = 1; v <= n; ++v)
{
if(edge[u][v])
{
double w = edge[u][v];
if(dis[v] > dis[u] + w)
{
dis[v] = dis[u] + w;
if(vis[v] == false)
{
vis[v] = true;
que.push(v);
}
}
}
}
}
}
int main()
{
init();
spfa();
cout << fixed << setprecision(2) << dis[te];
return 0;
}
解法4:Dijkstra堆优化算法+邻接表
#include<bits/stdc++.h>
using namespace std;
#define N 105
struct Cord
{
int x, y;
};
struct Edge
{
int t;
double w;
Edge(){}
Edge(int a, double b):t(a),w(b){}
};
struct Pair
{
int u;//u:顶点
double d;//距离
Pair(){}
Pair(int a, double b):u(a),d(b){}
bool operator < (const Pair &b) const
{
return b.d < d;
}
};
vector<Edge> edge[N];
int n, m, st, te;//st:起点 te:终点
double dis[N];//dis[i]:st到顶点i的最短距离
bool vis[N]; //vis[i]:i是否在队列内
Cord ver[N];//保存每个顶点的坐标
double getDis(Cord a, Cord b)//获取点a到点b两点间直线距离
{
return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
void init()
{
int f, t;
double w;
cin >> n;
for(int i = 1; i <= n; ++i)
cin >> ver[i].x >> ver[i].y;
cin >> m;
for(int i = 1; i <= m; ++i)
{
cin >> f >> t;
w = getDis(ver[f], ver[t]);
edge[f].push_back(Edge(t, w));
edge[t].push_back(Edge(f, w));
}
cin >> st >> te;
}
void dijkstra()
{
priority_queue<Pair> pq;
memset(dis, 0x43, sizeof(dis));//这样可以使dis中每个元素的值大约为1.08e16,是一个较大值
dis[st] = 0;
pq.push(Pair(st, 0));
while(pq.empty() == false)
{
int u = pq.top().u;
pq.pop();
if(vis[u])//如果顶点u已经访问过,则跳过
continue;
vis[u] = true;
for(int i = 0; i < edge[u].size(); ++i)
{
int v = edge[u][i].t;
double w = edge[u][i].w;
if(vis[v] == false && dis[v] > dis[u] + w)//松弛
{
dis[v] = dis[u] + w;
pq.push(Pair(v, dis[v]));
}
}
}
}
int main()
{
init();
dijkstra();
cout << fixed << setprecision(2) << dis[te];
return 0;
}