题目大意:求图的严格次短路。
方法1:
SPFA,同时求单源最短路径和单源次短路径。站在节点u上放松与其向量的v的次短路径时时,先尝试由u的最短路径放松,再尝试由u的次短路径放松(该两步并非非此即彼)。
由u的最短路径放松:
if(u->Dist + e->Weight < v->Dist) v->Dist2=v->Dist; //此处隐藏最短路放松。次短路不在此固定,Dist2可能在由u次短路放松时被放松得更短 if(u->Dist + e->Weight > v->Dist && u->Dist + e->Weight < v->Dist2) v->Dist2=u->Dist+e->Weight;
由u的次短路经放松:
if(u->Dist2 + e->Weight > v->Dist && u->Dist2 + e->Weight < v->Dist2) v->Dist2=u->Dist2 + e->Weight;
完整代码:
#include <cstdio> #include <cstring> #include <cassert> #include <queue> using namespace std; #define LOOP(i,n) for(int i=1; i<=n; i++) const int MAX_NODE = 5010, MAX_EDGE = 100010 * 2, INF = 0x3f3f3f3f; struct Node; struct Edge; struct Node { int Id, Dist, Dist2; bool Inq; Edge *Head; }_nodes[MAX_NODE], *Start, *Target; int _vCount; struct Edge { int Weight; Node *From, *To; Edge *Next; Edge() {} Edge(Node *from, Node *to, Edge *next, int weight) : From(from), To(to), Next(next), Weight(weight){} }*_edges[MAX_EDGE]; int _eCount; void Init(int vCount) { memset(_nodes, 0, sizeof(_nodes)); _vCount = vCount; _eCount = 0; Start = 1 + _nodes; Target = vCount + _nodes; } void AddEdge(Node *from, Node *to, int weight) { Edge *e = _edges[++_eCount] = new Edge(from, to, from->Head, weight); e->From->Head = e; } void Build(int uId, int vId, int weight) { Node *u = uId + _nodes, *v = vId + _nodes; u->Id = uId; v->Id = vId; AddEdge(u, v, weight); AddEdge(v, u, weight); } void SPFA() { LOOP(i, _vCount) _nodes[i].Dist = _nodes[i].Dist2 = INF; static queue<Node*> q; Start->Dist = 0; Start->Dist2 = INF; Start->Inq = true; q.push(Start); while (!q.empty()) { Node *u = q.front(); q.pop(); u->Inq = false; for (Edge *e = u->Head; e; e = e->Next) { bool relaxOk = false; if (u->Dist + e->Weight < e->To->Dist) { e->To->Dist2 = e->To->Dist; e->To->Dist = u->Dist + e->Weight; relaxOk = true; } else if (u->Dist + e->Weight > e->To->Dist && u->Dist + e->Weight < e->To->Dist2) { e->To->Dist2 = u->Dist + e->Weight; relaxOk = true; } if (u->Dist2 + e->Weight < e->To->Dist2) { e->To->Dist2 = u->Dist2 + e->Weight; relaxOk = true; } if (relaxOk && !e->To->Inq) { e->To->Inq = true; q.push(e->To); } } } } int main() { #ifdef _DEBUG freopen("c:\noi\source\input.txt", "r", stdin); #endif int testCase, totNode, totEdge, uId, vId, weight, sId, tId; scanf("%d%d", &totNode, &totEdge); Init(totNode); LOOP(i, totEdge) { scanf("%d%d%d", &uId, &vId, &weight); Build(uId, vId, weight); } SPFA(); printf("%d ", Target->Dist2); return 0; }
方法2:
Dijkstra。其需用到优先队列,维护一对数据:一个节点u以及它到原点的路径d。d可以是u的最短路径,也可以是u的次短路径,但我们不用管它,我们只管放松。它能放松v最短路就放松v最短路,再不行看看它能不能放松v次短路。
完整代码:
#include <cstdio> #include <cstring> #include <cassert> #include <queue> #include <vector> #include <functional> using namespace std; #define LOOP(i,n) for(int i=1; i<=n; i++) const int MAX_NODE = 5010, MAX_EDGE = 100010 * 2, INF = 0x3f3f3f3f; struct Node; struct Edge; struct Node { int Id, Dist, Dist2; bool Inq; Edge *Head; }_nodes[MAX_NODE], *Start, *Target; int _vCount; struct Edge { int Weight; Node *From, *To; Edge *Next; Edge() {} Edge(Node *from, Node *to, Edge *next, int weight) : From(from), To(to), Next(next), Weight(weight) {} }*_edges[MAX_EDGE]; int _eCount; void Init(int vCount) { memset(_nodes, 0, sizeof(_nodes)); _vCount = vCount; _eCount = 0; Start = 1 + _nodes; Target = vCount + _nodes; } void AddEdge(Node *from, Node *to, int weight) { Edge *e = _edges[++_eCount] = new Edge(from, to, from->Head, weight); e->From->Head = e; } void Build(int uId, int vId, int weight) { Node *u = uId + _nodes, *v = vId + _nodes; u->Id = uId; v->Id = vId; AddEdge(u, v, weight); AddEdge(v, u, weight); } #define Pair pair<int,Node*> void Dijkstra() { static priority_queue<Pair, vector<Pair>, greater<Pair>> q; LOOP(i, _vCount) _nodes[i].Dist = _nodes[i].Dist2 = INF; Start->Dist = 0; q.push(Pair(0, Start)); while (!q.empty()) { Pair cur = q.top(); q.pop(); Node *u = cur.second; int prevDist = cur.first; //printf("prevDist %d ", prevDist); assert(prevDist >= u->Dist); for (Edge *e = u->Head; e; e = e->Next) { if (prevDist + e->Weight < e->To->Dist) { e->To->Dist2 = e->To->Dist; e->To->Dist = prevDist + e->Weight; q.push(Pair(e->To->Dist, e->To)); } else if (e->To->Dist < prevDist+e->Weight &&prevDist+e->Weight < e->To->Dist2) { e->To->Dist2 = prevDist + e->Weight; q.push(Pair(e->To->Dist2, e->To)); } } } } int main() { #ifdef _DEBUG freopen("c:\noi\source\input.txt", "r", stdin); #endif int testCase, totNode, totEdge, uId, vId, weight, sId, tId; scanf("%d%d", &totNode, &totEdge); Init(totNode); LOOP(i, totEdge) { scanf("%d%d%d", &uId, &vId, &weight); Build(uId, vId, weight); } Dijkstra(); printf("%d ", Target->Dist2); return 0; }
方法3:
我们假设一条边e在次短路径内,知道这条次短路径长度d[e]是多少。那么,我们枚举每个e,求出min{d[e]}即可。具体我们需要求出每一个节点到原点的最短路径和到汇点的最短路径。这样,d[e]=e->From->DistS + e->Weight + e->To->DistT。这样求出的d[e]可能与最短路径相等。此时怎样把它转化成次短路?只能将e重复走一遍!这样,d[e]'=e->From->DistS + e->Weight * 2 + e->From->DistT。
完整代码:
#include <cstdio> #include <cstring> #include <cassert> #include <queue> #include <vector> #include <functional> using namespace std; #define LOOP(i,n) for(int i=1; i<=n; i++) const int MAX_NODE = 5010, MAX_EDGE = 100010 * 2, INF = 0x3f3f3f3f; struct Node; struct Edge; struct Node { int Id, Dist, Dist2; bool Inq; Edge *Head; }_nodes[MAX_NODE], *Start, *Target; int _vCount; struct Edge { int Weight; Node *From, *To; Edge *Next; Edge() {} Edge(Node *from, Node *to, Edge *next, int weight) : From(from), To(to), Next(next), Weight(weight) {} }*_edges[MAX_EDGE]; int _eCount; void Init(int vCount) { memset(_nodes, 0, sizeof(_nodes)); _vCount = vCount; _eCount = 0; Start = 1 + _nodes; Target = vCount + _nodes; } void AddEdge(Node *from, Node *to, int weight) { Edge *e = _edges[++_eCount] = new Edge(from, to, from->Head, weight); e->From->Head = e; } void Build(int uId, int vId, int weight) { Node *u = uId + _nodes, *v = vId + _nodes; u->Id = uId; v->Id = vId; AddEdge(u, v, weight); AddEdge(v, u, weight); } void SPFA(Node *start) { LOOP(i, _vCount) { _nodes[i].Dist = INF; _nodes[i].Inq = false; } static queue<Node*> q; start->Dist = 0; start->Inq = true; q.push(start); while (!q.empty()) { Node *u = q.front(); q.pop(); u->Inq = false; for (Edge *e = u->Head; e; e = e->Next) { if (u->Dist + e->Weight < e->To->Dist) { e->To->Dist = u->Dist + e->Weight; if (!e->To->Inq) { e->To->Inq = true; q.push(e->To); } } } } } int Proceed() { SPFA(Target); int minDist = Start->Dist, ans = INF; LOOP(i, _vCount) _nodes[i].Dist2 = _nodes[i].Dist; SPFA(Start); LOOP(i, _eCount) { Edge *e = _edges[i]; int temp = e->From->Dist + e->Weight + e->To->Dist2; if (minDist < temp&&temp < ans) ans = temp; else { temp = e->From->Dist + e->From->Dist2 + e->Weight * 2; ans = min(ans, temp); } } return ans; } int main() { #ifdef _DEBUG freopen("c:\noi\source\input.txt", "r", stdin); #endif int testCase, totNode, totEdge, uId, vId, weight, sId, tId; scanf("%d%d", &totNode, &totEdge); Init(totNode); LOOP(i, totEdge) { scanf("%d%d%d", &uId, &vId, &weight); Build(uId, vId, weight); } printf("%d ", Proceed()); return 0; }