问题描述
蒜头君陷入了坐标系上的一个迷阵,迷阵上有 n 个点,编号从 1 到 n。蒜头君在编号为 1 的位置,他想到编号为 n 的位置上。蒜头君当然想尽快到达目的地,但是他觉得最短的路径可能有风险,所以他会选择第二短的路径。现在蒜头君知道了 n 个点的坐标,以及哪些点之间是相连的,他想知道第二短的路径长度是多少。
注意,每条路径上不能重复经过同一个点。
输入格式
第一行输入两个整数 n (1≤n≤200) 和 m,表示一共有 n 个点和 m 条边。
接下来输入 n 行,每行输入两个整数xi,yi(−500≤xi,yi≤500),代表第 ii 个点的坐标。
接下来输入 mm 行,每行输入两个整数 pj,qj(1≤pj,qj≤n),表示点pj和点 qj之间相连。
输出格式
输出一行,输出包含一个数,表示第二短的路径长度(小数点后面保留两位),如果第一短路径有多条,则答案就是第一最短路径的长度;如果第二最短路径不存在,则输出 −1。
样例输入
3 3
1 1
2 2
3 2
1 2
2 3
1 3
样例输出
2.41思路
先求出最短路,然后在依次删除这条最短路上的点及相连的边,每删一求次出此时的最短路即为次短路,取所有次短路中最小值即为此图的次短路,提示:对最短路为起点到终点仅有一条有向边的情况需要特殊处理
#include<bits/stdc++.h>
#include<cstring>
using namespace std;
const int N = 205;
vector<int> v_pos[N];
double g[N][N];
double dis[N];
bool vis[N];
int n, m;
int pre[N]; //用一个数组记录当前点是由哪个点更新的,例如pre[2] = 1 ,即结点2由1更新得到
void Dijkstra(int u,int dele_v){
memset(dis,0x7f,sizeof dis);
dis[u] = 0;
pre[1] = 1; //结点1由自身得到的
for(int i = 0 ;i < n; ++i){
double min_d = dis[0];
int min_v = -1;
for(int j = 1; j <= n; ++j){
if(j != dele_v && !vis[j] && dis[j] < min_d){
min_d = dis[j];
min_v = j;
}
}
if(min_v == -1){ //说明单源最短路已经找齐
return;
}
vis[min_v] = true; //标记
for(int k = 1; k <= n; ++k){ //遍历和j相邻的点
if(k != dele_v && min_v != k && !vis[k] && dis[k] > min_d + g[min_v][k]){
dis[k] = min_d + g[min_v][k];
pre[k] = min_v;
}
}
}
}
int main(){
memset(g,0x7f,sizeof g);
cin >> n >> m;
int i = 1;
int t = n;
while(t--){
int x,y;
cin >> x >> y;
v_pos[i].push_back(x);
v_pos[i].push_back(y);
++i;
}
while(m--){
int u,v;
cin >> u >> v;
double len = sqrt((v_pos[u][0]-v_pos[v][0])*(v_pos[u][0]-v_pos[v][0])+(v_pos[u][1]-v_pos[v][1])*(v_pos[u][1]-v_pos[v][1]));
g[u][v] = g[v][u] = len;
}
Dijkstra(1,0);
double ans = g[0][0];
if(pre[n] == 1){ //如果刚好从1到n最短路前一个结点就是1,即最短路就是1和n的边权,需要特殊处理
g[1][n] = g[n][1] = g[0][0]; //删除从1到n的无向边
memset(vis,0,sizeof vis); //注意!!!! 每用一次Dijkstra,就得重置一次vis
Dijkstra(1,0);
ans = min(ans,dis[n]);
}
else{ //否则,依次删去最短路除头尾结点的各个结点,再进行Dijkstra算法,找出删去结点的最短路的最小值,即次短路
for(int p = pre[n]; p != 1; p = pre[p]){
memset(vis,0,sizeof vis); //注意!!!! 每用一次Dijkstra,就得重置一次vis
Dijkstra(1,p); //对最短路上有其他点的情况,只需依次删除点即可
ans = min(dis[n], ans);
}
}
printf("%.2f\n",ans);
return 0;
}