luogu2770 航空路线问题 网络流
题目大意:
给定一张航空图,图中顶点代表城市,边代表 2 城市间的直通航线。现要求找出一条满足下述限制条件的且途经城市最多的旅行路线。
(1)从最西端城市出发,单向从西向东途经若干城市到达最东端城市,然后再单向从东向西飞回起点(可途经若干城市)。
(2)除起点城市外,任何城市只能访问 1 次。
关键字:网络流 方向等效转换 拆点 不交叉路径 特判
网络流:1个人旅行的过程可以看作以流量为1流动的过程。
方向等效转化:本问题可以看作两个人同时从最西的城市走向最东的城市,每个人占有1个流量,总流量为2。
拆点:题中说每个城市只经过一次,因此把一个城市看作容量为1的边,如果西东两个城市之间有航线,则把西城to节点连东城from节点,容量为1。为保证总流量为2,最西城边容量和最东城容量为2。
不交叉路径:流量为1,即可保证路径不交叉
特判:如果最西城与最东城有航线,则边的容量为2。
#include <cstdio> #include <cstring> #include <queue> #include <cmath> #include <cassert> #include <map> #include <string> #include <iostream> using namespace std; //#define test #define INF 0x3f3f3f3f #define LOOP(i,n) for(int i=1; i<=n; i++) const int MAX_CITY = 110, MAX_EDGE = MAX_CITY*MAX_CITY + MAX_CITY * 2, MAX_NODE = MAX_CITY * 2; map<string, int> loc; string Cities[MAX_CITY]; bool Vis[MAX_CITY]; int TotCity, TotLine; struct MCMF { struct Node; struct Edge; struct Node { int Id; Edge *Head, *Prev; int Dist; bool Inq; void Init() { Prev = NULL; Dist = INF; Inq = false; } }; struct Edge { int Cap, Cost; Node *From, *To; Edge *Next, *Rev; Edge(int cap, int cost, Node *from, Node *to, Edge *next) :Cap(cap), Cost(cost), From(from), To(to), Next(next){} }; Node _nodes[MAX_NODE]; Edge *_edges[MAX_EDGE]; int _vCount = 0, _eCount = 0; Node *Start, *Sink; int TotFlow, TotCost; void Reset(int n, int sId, int tId) { _vCount = n; _eCount = 0; Start = &_nodes[sId], Sink = &_nodes[tId]; TotFlow = TotCost = 0; } Edge* AddEdge(Node *from, Node *to, int cap, int cost) { Edge *cur = _edges[++_eCount] = new Edge(cap, cost, from, to, from->Head); from->Head = cur; return cur; } void Build(int uId, int vId, int cap, int cost) { Node *u = &_nodes[uId], *v = &_nodes[vId]; u->Id = uId; v->Id = vId; Edge *edge1 = AddEdge(u, v, cap, cost); Edge *edge2 = AddEdge(v, u, 0, -cost);//遗忘点*2:-cost edge1->Rev = edge2; edge2->Rev = edge1; } bool SPFA() { queue<Node*> q; for (int i = 1; i <= _vCount; i++) _nodes[i].Init(); Start->Dist = 0; 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 (e->Cap && u->Dist + e->Cost < e->To->Dist)//易忘点*2:e->Cap { e->To->Dist = u->Dist + e->Cost; e->To->Prev = e; if (!e->To->Inq) { e->To->Inq = true; q.push(e->To); } } } } return Sink->Prev; } void Proceed() { while (SPFA()) { assert(Sink->Dist != INF); int minFlow = INF; for (Edge *e = Sink->Prev; e; e = e->From->Prev) minFlow = min(minFlow, e->Cap); TotFlow += minFlow; for (Edge *e = Sink->Prev; e; e = e->From->Prev) { e->Cap -= minFlow; e->Rev->Cap += minFlow; TotCost += minFlow * e->Cost; #ifdef test printf("%d-%d Cost %d restCap %d\n", e->From->Id, e->To->Id, e->Cost*minFlow, e->Cap); #endif } #ifdef test printf("TotFlow %d TotCost %d\n", TotFlow, TotCost); #endif } } int GetFlow() { return TotFlow; } int GetCost() { return TotCost; } }g; void print1() { MCMF::Node *cur = 1 + g._nodes; while (cur->Id != TotCity) { Vis[cur->Id] = true; cout << Cities[cur->Id] << endl; cur = cur->Id + TotCity + g._nodes; for (MCMF::Edge *e = cur->Head; e; e = e->Next) { if (!e->Cap && !Vis[e->To->Id] && e->To->Id > cur->Id-TotCity) { cur = e->To; break; } } } } void print2(int id) { if (id == TotCity) { cout << Cities[id] << endl; return; } Vis[id] = true; MCMF::Node *cur = id + TotCity + g._nodes; for (MCMF::Edge *e = cur->Head; e; e = e->Next) { if (!e->Cap && !Vis[e->To->Id] && e->To->Id > cur->Id - TotCity) { print2(e->To->Id); break; } } cout << Cities[id] << endl; } int main() { #ifdef _DEBUG freopen("c:\\noi\\source\\input.txt", "r", stdin); #endif int sId, tId; string s1, s2; scanf("%d%d", &TotCity, &TotLine); sId = 1; tId = TotCity * 2; g.Reset(tId, sId, tId); LOOP(city, TotCity) { cin >> Cities[city]; loc.insert(pair<string, int>(Cities[city], city)); if (city == 1 || city == TotCity) g.Build(city, city + TotCity, 2, -1); else g.Build(city, city + TotCity, 1, -1); } LOOP(line, TotLine) { cin >> s1 >> s2; int p1 = loc[s1], p2 = loc[s2]; if (p2 < p1) swap(p1, p2); if (p1 == 1 && p2 == TotCity) g.Build(p1+TotCity, p2, 2, 0); else g.Build(p1+TotCity, p2, 1, 0); } g.Proceed(); if (g.TotFlow<2) { cout << "No Solution!" << endl; return 0; } printf("%d\n", -g.TotCost-2); LOOP(v, g._vCount) g._nodes[v].Inq = false; print1(); print2(1); return 0; }