网络流24题(十一)
网络流24题(十一)
十一、航空路线问题
题目描述
给定一张航空图,图中顶点代表城市,边代表两城市间的直通航线,并且不存在任何两个城市在同一条经线上。现要求找出一条满足下述限制条件的且途经城市最多的旅行路线。
-
从最西端城市出发,单向从西向东途经若干城市到达最东端城市,然后再单向从东向西飞回起点(可途经若干城市)。
-
除起点城市外,任何城市只能访问一次。
对于给定的航空图,试设计一个算法找出一条满足要求的最佳航空旅行路线。
输入格式
输入的第一行是用空格隔开的两个整数,分别代表航空图的点数 \(n\) 和边数 \(v\)。
第 \(2\) 到第 \((n + 1)\) 行,每行一个字符串,第 \((i + 1)\) 行的字符串代表从西向东第 \(i\) 座城市的名字 \(s_i\)。
第 \((n + 2)\) 到第 \((n + v + 1)\) 行,每行两个字符串 \(x, y\)代表城市 \(x\) 和城市 \(y\) 之间存在一条直通航线。
输出格式
本题存在 Special Judge。
请首先判断是否存在满足要求的路线,若存在,请给出一种旅行的方案。
如果存在路线,输出格式为:
- 请在第一行输出一个整数 \(m\),代表途径最多的城市数。
- 在第 2 到第 \((m + 1)\) 行,每行一个字符串,第 \((i + 1)\) 行的字符串代表旅行路线第 \(i\) 个经过的城市的名字。请注意第 \(1\) 和第 \(m\) 个城市必然是出发城市名。
否则请输出一行一个字符串 No Solution!
。
题解
模型:
费用流、拆点、输出方案
双路\(DP\)。
建图与实现:
只有两条路径,想到限制流为2。
每一个点都有限制,即只能选一次,选了之后得分加一,所以想到拆点限制点的容量。
对于一个点\(i\),拆成\(i\)与\(i+n\),容量为一,费用为1。但特殊点起点与终点会出现两次(题面说起点可以有多次,但是想一下在最大流上限为2的情况下,最多也只有两次)。但是这时候多的不加费用,所以再加上一条容量为1,费用为0的边。
最后在题目给出的图上的边,限制为容量无穷,费用为0。
最后跑最大费用最大流,如果得到的流小于2,说明没有两条路径,输出 No Solution!
。
输出方案比较恶心,但不算很难(好吧我承认我wa在这)。
代码
#include <iostream>
#include <queue>
#include <cstring>
#include <map>
#include <stack>
#define ll long long
const ll N = 5e3+50,M = 5e4+50;
const ll inf = 0x3f3f3f3f;
using namespace std;
ll head[N],cnt = 1;
//将EK的bfs变为spfa
struct Edge{
ll to,w,cost,nxt;
}edge[M*2];
void add(ll u,ll v,ll w,ll c){
edge[++cnt] = {v,w,c,head[u]};
head[u] = cnt;
}
void add2(ll u,ll v,ll w,ll cost){
add(u,v,w,cost);
add(v,u,0,-cost);
}
ll s,t,dis[N],cur[N];
bool inq[N],vis[N];
queue<ll>Q;
bool spfa(){
while(!Q.empty()) Q.pop();
copy(head,head+N,cur);
fill(dis,dis+N,inf);
dis[s] = 0;
Q.push(s);
while(!Q.empty()){
ll p = Q.front();
Q.pop();
inq[p] = false;
for(ll e = head[p];e;e = edge[e].nxt){
ll to = edge[e].to,vol = edge[e].w;
if(vol > 0 && dis[to]>dis[p]+edge[e].cost){
dis[to] = dis[p] + edge[e].cost;
if(!inq[to]){
Q.push(to);
inq[to] = true;
}
}
}
}
return dis[t] != inf;
}
ll dfs(ll p = s,ll flow = inf){
if(p == t) return flow;
vis[p] = true;
ll rmn = flow;
for(ll eg = cur[p];eg && rmn;eg = edge[eg].nxt){
cur[p] = eg;
ll to = edge[eg].to,vol = edge[eg].w;
if(vol > 0 && !vis[to]&&dis[to] == dis[p]+edge[eg].cost){
ll c = dfs(to,min(vol,rmn));
rmn -= c;
edge[eg].w -= c;
edge[eg^1].w += c;
}
}
vis[p] = false;
return flow-rmn;
}
ll maxflow = 0,mincost = 0;
void dinic(){
while(spfa()){
ll flow = dfs();
maxflow += flow;
mincost += dis[t]*flow;
}
}
ll n,m;
string city[N];
map<string,ll> num;
queue<ll>tour1;
stack<ll>tour2;
bool inTr[N];
void find1(ll x){
for(ll eg = head[x+n];eg;eg = edge[eg].nxt){
ll to = edge[eg].to,vol = edge[eg].w;
if(vol == inf) continue;
if(inTr[to])continue;
for(ll j = head[to];j;j = edge[j].nxt){
if(edge[j].w == 0 && edge[j].to == to+n){
tour1.push(to);
inTr[to] = true;
find1(to);
return;
}
}
}
}
void find2(ll x){
for(ll eg = head[x+n];eg;eg = edge[eg].nxt){
ll to = edge[eg].to,vol = edge[eg].w;
if(vol == inf) continue;
if(inTr[to])continue;
for(ll j = head[to];j;j = edge[j].nxt){
if(edge[j].w == 0 && edge[j].to == to+n){
tour2.push(to);
inTr[to] = true;
find2(to);
return;
}
}
}
}
void print(){
inTr[1] = true;
find1(1);
find2(1);
cout<<city[1]<<endl;
while(!tour1.empty()){
ll x = tour1.front();
tour1.pop();
cout<<city[x]<<endl;
}
while(!tour2.empty()){
ll x = tour2.top();
tour2.pop();
cout<<city[x]<<endl;
}
cout<<city[1]<<endl;
}
int main() {
cin>>n>>m;
for(ll i = 1;i <= n;i++){
cin>>city[i];
num[city[i]] = i;
}
while(m--){
string x,y;cin>>x>>y;
ll u = num[x],v = num[y];
//cout<<u<<' '<<v<<endl;
add2(u+n,v,inf,0);
}
s = 1,t = 2*n;
add2(1,n+1,1,-1);
add2(1,n+1,1,0);
add2(n,n+n,1,-1);
add2(n,n+n,1,0);
for(ll i = 2;i < n;i++) add2(i,i+n,1,-1);
dinic();
// cout<<maxflow<<' '<<mincost<<endl;
// if(mincost == -4){
// cout<<city[1]<<endl<<city[n]<<endl<<city[1]<<endl;
// }
if(maxflow != 2) {
puts("No Solution!");
return 0;
}
cout<<-1*mincost<<endl;
print();
return 0;
}