蒜头君陷入了坐标系上的一个迷阵,迷阵上有 n 个点,编号从 1 到 n。蒜头君在编号为 1 的位置,他想到编号为 n 的位置上。蒜头君当然想尽快到达目的地,但是他觉得最短的路径可能有风险,所以他会选择第二短的路径。现在蒜头君知道了 n 个点的坐标,以及哪些点之间是相连的,他想知道第二短的路径长度是多少。
注意,每条路径上不能重复经过同一个点。
输入格式
第一行输入两个整数 n (1≤n≤200) 和 m,表示一共有 n 个点和 m 条边。
接下来输入 n 行,每行输入两个整数 x_i, y_i(−500≤ x_i, y_i ≤500),代表第 i 个点的坐标。
double mp[N][N]; double dis[N]; bool vis[N]; int pre[N]; //用来记录最短路中每一个结点的前驱 int tmp[N]; typedefpair<int, int> PII; set<PII, less<PII> > min_heap; int n, m; int x[N]; int y[N];
doubledistance(int a, int b){ returnsqrt((x[a] - x[b]) * (x[a] - x[b]) + (y[a] - y[b]) * (y[a] - y[b])); }
booldijstra(int u){ for (int i = 1; i <= n; i++) { dis[i] = INF; } memset(vis, false, sizeof vis); memset(pre, -1, sizeof pre); dis[u] = 0.0; min_heap.insert(make_pair(0.0, u)); for (int i = 0; i < n; i++) { if (min_heap.size() == 0) { returnfalse; } set<PII, less<PII> >::iterator it = min_heap.begin(); int min_v = it->second; vis[min_v] = true; min_heap.erase(*it);
for (int i = 1; i <= n; i++) { if (!vis[i] && dis[i] > dis[min_v] + mp[min_v][i]) { pre[i] = min_v; // 记录前驱 min_heap.erase(make_pair(dis[i], i)); dis[i] = dis[min_v] + mp[min_v][i]; min_heap.insert(make_pair(dis[i], i)); } } } returntrue; }
intmain(){ cin >> n >> m; for (int i = 1; i <= n; i++) { cin >> x[i] >> y[i]; } for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { mp[i][j] = INF; } } int u, v; for (int i = 0; i < m; i++) { cin >> u >> v; mp[u][v] = mp[v][u] = distance(u, v); } if (dijstra(1) == false) { cout << -1 << endl; return0; }
for (int i = 1; i <= n; i++) { // 因为 pre[i] 会被后面的 dijstra() 操作重新覆盖 tmp[i] = pre[i]; }
double ans = INF; int now = n; //枚举两个顶点之间最短路上的每条边,每次在去掉这条边的剩下的图中计算最短路 while (tmp[now] + 1) { //因为 tmp[起始点] = -1, 当now = 起始点时,就会停止循环 mp[now][tmp[now]] = mp[tmp[now]][now] = INF; //去掉这条边 dijstra(1); ans = min(ans, dis[n]); //取其中最小的一个答案就是最终次短路的答案 mp[now][tmp[now]] = mp[tmp[now]][now] = distance(now, tmp[now]); //还原这条边,以进行下一个 now = tmp[now]; }