题目大意:
给定一张航空图,图中顶点代表城市,边代表 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:-costedge1->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 testprintf("%d-%d Cost %d restCap %d\n", e->From->Id, e->To->Id, e->Cost*minFlow, e->Cap); #endif} #ifdef testprintf("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 _DEBUGfreopen("c:\\noi\\source\\input.txt", "r", stdin); #endifint 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);elseg.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);elseg.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; }