题目大意
在一个无向图中,定义两个点s,t的最短路径子图为一个极大边集,对于该边集内的所有有向边e,总存在一条起点为s,终点为t且经过边e的路径,使得该路径长度为s到t的最短路径长度。现给出一个无向图,s1, s2, t1, t2四个节点,求一条最长的路径,使得它满足下列条件之一:1. 该路径上的所有边都属于s1到t1的最短路径子图且属于s2到t2的最短路径子图;2. 该路径上的所有边都属于s1到t1的最短路径子图且属于t2到s2的最短路径子图。输出该路径的长度。
题解
我们可以用4次Dijkstra得到s1,t1和s2,t2的最短路径子图$G_1,G_2$,另外由$G_2$可以得到其反向图$G'_2$。然后分别在子图$G_1cap G_2$和$G_1cap G'_2$上进行拓扑排序求最长路径即可。本题最重要的就是从题面到数学语言的翻译过程了,如果这一点不明确,我们可能就会建立一个子图$G_1cap(G_2cup G'_2)$,这样就乱了。
#include <cstdio> #include <cstring> #include <algorithm> #include <queue> #include <stack> #include <cassert> using namespace std; const int MAX_NODE = 1510, MAX_EDGE = 200005, INF = 0x3f3f3f3f; struct Node; struct Edge; struct Node { int DistS, DistT; bool Done; int Dist;//Dijkstra时,它是临时的东东;TopSort时,它是真正的最长路径长度 int DfsN; Edge *Head; }_nodes[MAX_NODE]; int TotNode; struct HeapNode { Node *cur; int Dist; HeapNode(Node *x):cur(x),Dist(x->Dist){} bool operator < (const HeapNode& a) const { return Dist > a.Dist; } }; struct Edge { int Weight; bool InSPG;//在第一个人的最短路径子图中 bool InTopG;//在Top子图中 Node *To, *From; Edge *Next, *Rev; Edge() :InTopG(false), InSPG(false){} }_edges[MAX_EDGE]; vector<Edge*> Temp; int _eCount; Edge *NewEdge() { if (_eCount < MAX_EDGE - 1) return _edges + ++_eCount; else { Temp.push_back(new Edge()); return Temp.back(); } } Edge *AddEdge(Node *from, Node *to, int w) { Edge *e = NewEdge(); e->From = from; e->To = to; e->Weight = w; e->Next = from->Head; from->Head = e; return e; } void Build(int uId, int vId, int w) { Node *u = _nodes + uId, *v = _nodes + vId; Edge *e1 = AddEdge(u, v, w), *e2 = AddEdge(v, u, w); e1->Rev = e2; e2->Rev = e1; } void Dijkstra(Node *start) { static priority_queue<HeapNode> q; for (int i = 1; i <= TotNode; i++) { _nodes[i].Dist = INF; _nodes[i].Done = false; } start->Dist = 0; q.push(start); while (!q.empty()) { HeapNode temp = q.top(); q.pop(); Node *cur = temp.cur; if (cur->Done) continue; cur->Done = true; for (Edge *e = cur->Head; e; e = e->Next) { if (cur->Dist + e->Weight < e->To->Dist) { e->To->Dist = cur->Dist + e->Weight; q.push(e->To); } } } } void GetInGraph(void(*DoInGraph)(Edge*), int spLen) { for (int i = 1; i <= _eCount; i++) if (_edges[i].From->DistS + _edges[i].Weight + _edges[i].To->DistT == spLen) DoInGraph(_edges + i); for (int i = 0; i < Temp.size(); i++) if (Temp[i]->From->DistS + Temp[i]->Weight + Temp[i]->To->DistT == spLen) DoInGraph(Temp[i]); } void SetOrgGraph(Edge *e) { e->InSPG = true; } void MakeTopGraph1(Edge *e) { if (e->InSPG) e->InTopG = true; } void MakeTopGraph2(Edge *e) { if (e->Rev->InSPG) e->InTopG = true; } int ShortestPath(int s, int t) { Node *start = _nodes + s, *target = _nodes + t; Dijkstra(target); for (int i = 1; i <= TotNode; i++) _nodes[i].DistT = _nodes[i].Dist; Dijkstra(start); for (int i = 1; i <= TotNode; i++) _nodes[i].DistS = _nodes[i].Dist; return target->DistS; } void ClearTopGraph() { for (int i = 1; i <= _eCount; i++) _edges[i].InTopG = false; for (int i = 0; i < Temp.size(); i++) Temp[i]->InTopG = false; } stack<Node*> St; void Dfs(Node *cur) { assert(cur->DfsN != 1); if (cur->DfsN == 2) return; cur->DfsN = 1; for (Edge *e = cur->Head; e; e = e->Next) { if (!e->InTopG) continue; Dfs(e->To); } cur->DfsN = 2; St.push(cur); } int LongestPath() { for (int i = 1; i <= TotNode; i++) { _nodes[i].Dist = 0; _nodes[i].DfsN = 0; } for (int i = 1; i <= TotNode; i++) Dfs(_nodes + i); int ans = 0; while (!St.empty()) { Node *cur = St.top(); St.pop(); ans = max(ans, cur->Dist); for (Edge *e = cur->Head; e; e = e->Next) if (e->InTopG) e->To->Dist = max(e->To->Dist, cur->Dist + e->Weight); } return ans; } int main() { int totEdge, s1, s2, t1, t2; scanf("%d%d%d%d%d%d", &TotNode, &totEdge, &s1, &t1, &s2, &t2); for (int i = 1; i <= totEdge; i++) { int u, v, w; scanf("%d%d%d", &u, &v, &w); Build(u, v, w); } int len1 = ShortestPath(s1, t1); GetInGraph(SetOrgGraph, len1); int len2 = ShortestPath(s2, t2); GetInGraph(MakeTopGraph1, len2); int ans1 = LongestPath(); ClearTopGraph(); GetInGraph(MakeTopGraph2, len2); int ans2 = LongestPath(); printf("%d ", max(ans1, ans2)); return 0; }