次短路问题(最短路)
第5题 次短路问题 查看测评数据信息
平面直角坐标系中有n个点,m条边,第i个点的坐标是(x[i],y[i]),求编号为1的点到编号为n的点第二最短路线的距离(保留两位小数),如果存在多条第一短路径,则答案就是第一最短路径的长度;数据没有重边,可能有自环
输入格式
第一行两个数n和m
接下来n行,每行两个整数x[i],y[i]
接下来m行,每行两个整数u,v,表示u和v之间有一条路径
其中1<=n<=200,1<=m<=10000, -500<=x[i],y[i]<=500
输出格式
一行一个数(保留两位小数)
输入/输出例子1
输入:
3 3
0 0
1 1
0 2
1 2
1 3
2 3
输出:
2.83
样例解释
无
一般用删边法,这里采用枚举法
我们想求次短路,也就是在跑最短路的过程中加上一条边呗,转换成以下式子:
从1到u的距离我们在第一次跑最短路的时候可以求,v到n的距离可以从n跑一遍然后求一遍,u到v的距离就单纯枚举边
注意,如果有多条最短路,得输出最短路,可以在第一次跑的时候最短路计数
#include <bits/stdc++.h> using namespace std; const int N=205; struct node { int v; double w; bool operator <(const node &A) const { return w>A.w; }; }; struct node2 { int u, v; double w; }; int n, m, x[N], y[N], u1, v1, cnt[N], vis[N]; double dis[N], dis2[N], ans=1e9; vector<node> a[N]; vector<node2> s; priority_queue<node> q; double js(int u1, int v1) { double a=abs(x[u1]*1.0-x[v1]*1.0)*1.0; double b=abs(y[u1]*1.0-y[v1]*1.0)*1.0; return sqrt(a*a+b*b)*1.0; } void dij(int s) { for (int i=0; i<=N-5; i++) dis[i]=1e9; memset(vis, 0, sizeof vis); memset(cnt, 0, sizeof cnt); dis[s]=0; q.push({s, 0}); while (!q.empty()) { int u=q.top().v; q.pop(); if (vis[u]) continue; vis[u]=1; for (int i=0; i<a[u].size(); i++) { int v=a[u][i].v; double w=a[u][i].w; if (dis[v]==dis[u]+w) cnt[v]++; if (dis[v]>dis[u]+w) { cnt[v]=1; dis[v]=dis[u]+w; q.push({v, dis[v]}); } } } } int main() { scanf("%d%d", &n, &m); for (int i=1; i<=n; i++) scanf("%d%d", &x[i], &y[i]); for (int i=1; i<=m; i++) { scanf("%d%d", &u1, &v1); s.push_back({u1, v1, js(u1, v1)}); a[u1].push_back({v1, js(u1, v1)}); a[v1].push_back({u1, js(u1, v1)}); } dij(1); if (cnt[n]>1) { printf("%.2lf", dis[n]); return 0; } for (int i=1; i<=n; i++) dis2[i]=dis[i]; dij(n); for (int i=0; i<s.size(); i++) { int u=s[i].u, v=s[i].v; double w=s[i].w; if(dis2[u]+w+dis[v]>dis2[n]) ans=min(ans, dis2[u]+w+dis[v]); //记得加上判断!不是最短路才行,这里卡了我好久 } printf("%.2lf", ans); return 0; } /* 3 3 0 0 1 1 0 2 1 2 1 3 2 3 */