图论-次短路求法
测试题目 NKOJ1107 达喀尔拉力赛
方法一 Bellman-Ford. 计算 S 到任意点的最短路得到 dis[i], 计算任意点到 T 的最短路 sid[i], 枚举每一条边,假设这条边从 x 到 y, 权值 w.
显然次短路长度就是 min{ dis[x] + w + sid[y] } .这个方法代码非常好写,也很好想,不过跑了两次最短路,常数略大。
方法二 Dijkstra. dis 表示最短路, dis2 表示次短路, 见代码.
1 void Dijkstra(int beg) 2 { 3 LL i; 4 for (i = 1; i <= n; ++i) dis[i] = dis2[i] = INF; 5 dis[beg] = 0;//注意这里不能写 dis2[beg] = 0,想一想为什么(笑 6 while (!Q.empty()) Q.pop(); 7 Q.push(strdis(beg, dis[beg])); 8 while (!Q.empty()) { 9 strdis p = Q.top(); Q.pop(); 10 if (p.dis > dis2[p.v]) continue;////注意这里是 p.dis ,不能写 dis[p.v] ,因为当前取出的 p.dis 表示的也可能是 dis2[p.v] ,这与最短路写法中 p.dis 等价于 dis[p.v] 不同 11 for (i = 0; i < G[p.v].size(); ++i) { 12 LL tmp = p.dis + G[p.v][i].w;//注意这里是 p.dis ,不能写 dis[p.v] ,因为当前取出的 p.dis 表示的也可能是 dis2[p.v] ,这与最短路写法中 p.dis 等价于 dis[p.v] 不同 13 if (dis[G[p.v][i].v] > tmp) { 14 swap(dis[G[p.v][i].v], tmp);//注意这里最短路更新后,原来的值应该交换到 tmp 中,用于下面的 if 语句里更新次短路 15 Q.push(strdis(G[p.v][i].v, dis[G[p.v][i].v])); 16 } 17 if (dis[G[p.v][i].v] < tmp && dis2[G[p.v][i].v] > tmp) { 18 dis2[G[p.v][i].v] = tmp; 19 Q.push(strdis(G[p.v][i].v, dis2[G[p.v][i].v]));//注意 dis2 也应当 push 进队列,例子如下 20 //例子:dis[5] = 100, dis2[5] = 200 21 //有一条边 5 到 8 的边权值为 50 22 //如果不把 dis2 入队,那么之后的算法中 dis[8] = 150, dis2[8] = INF 23 //只有当队列里有 200 这个值,才能 200+50 得出 250,然后更新 dis2[8] = 250 24 } 25 } 26 } 27 return ; 28 }
完整代码:
1 #include <stdio.h> 2 #include <vector> 3 #include <queue> 4 5 using namespace std; 6 7 typedef long long LL; 8 9 const int _N = 600; 10 const long long INF = 99999999999999LL; 11 12 LL n, m; 13 LL dis[_N], dis2[_N]; 14 15 struct node { 16 LL v, w; 17 node(const LL &_v, const LL &_w): 18 v(_v), w(_w) {} 19 }; 20 21 struct strdis { 22 LL v, dis; 23 strdis(const LL &_v, const LL &_dis): 24 v(_v), dis(_dis) {} 25 bool operator < (const strdis &tmp) const 26 { 27 return this->dis > tmp.dis; 28 } 29 }; 30 31 vector<node> G[_N]; 32 priority_queue<strdis> Q; 33 34 void Dijkstra(int beg) 35 { 36 LL i; 37 for (i = 1; i <= n; ++i) dis[i] = dis2[i] = INF; 38 dis[beg] = 0;//注意这里不能写 dis2[beg] = 0,想一想为什么(笑 39 while (!Q.empty()) Q.pop(); 40 Q.push(strdis(beg, dis[beg])); 41 while (!Q.empty()) { 42 strdis p = Q.top(); Q.pop(); 43 if (p.dis > dis2[p.v]) continue;////注意这里是 p.dis ,不能写 dis[p.v] ,因为当前取出的 p.dis 表示的也可能是 dis2[p.v] ,这与最短路写法中 p.dis 等价于 dis[p.v] 不同 44 for (i = 0; i < G[p.v].size(); ++i) { 45 LL tmp = p.dis + G[p.v][i].w;//注意这里是 p.dis ,不能写 dis[p.v] ,因为当前取出的 p.dis 表示的也可能是 dis2[p.v] ,这与最短路写法中 p.dis 等价于 dis[p.v] 不同 46 if (dis[G[p.v][i].v] > tmp) { 47 swap(dis[G[p.v][i].v], tmp);//注意这里最短路更新后,原来的值应该交换到 tmp 中,用于下面的 if 语句里更新次短路 48 Q.push(strdis(G[p.v][i].v, dis[G[p.v][i].v])); 49 } 50 if (dis[G[p.v][i].v] < tmp && dis2[G[p.v][i].v] > tmp) { 51 dis2[G[p.v][i].v] = tmp; 52 Q.push(strdis(G[p.v][i].v, dis2[G[p.v][i].v]));//注意 dis2 也应当 push 进队列,例子如下 53 //例子:dis[5] = 100, dis2[5] = 200 54 //有一条边 5 到 8 的边权值为 50 55 //如果不把 dis2 入队,那么之后的算法中 dis[8] = 150, dis2[8] = INF 56 //只有当队列里有 200 这个值,才能 200+50 得出 250,然后更新 dis2[8] = 250 57 } 58 } 59 } 60 return ; 61 } 62 63 int main() 64 { 65 LL i, t1, t2, t3; 66 scanf("%lld%lld", &n, &m); 67 for (i = 1; i <= m; ++i) { 68 scanf("%lld%lld%lld", &t1, &t2, &t3); 69 G[t1].push_back(node(t2, t3)); 70 G[t2].push_back(node(t1, t3)); 71 } 72 Dijkstra(1); 73 printf("%lld\n", dis2[n]); 74 return 0; 75 }