網路流24題(十一)
阿新 • • 發佈:2021-10-05
網路流24題(十一)
十一、航空路線問題
題目描述
給定一張航空圖,圖中頂點代表城市,邊代表兩城市間的直通航線,並且不存在任何兩個城市在同一條經線上。現要求找出一條滿足下述限制條件的且途經城市最多的旅行路線。
-
從最西端城市出發,單向從西向東途經若干城市到達最東端城市,然後再單向從東向西飛回起點(可途經若干城市)。
-
除起點城市外,任何城市只能訪問一次。
對於給定的航空圖,試設計一個演算法找出一條滿足要求的最佳航空旅行路線。
輸入格式
輸入的第一行是用空格隔開的兩個整數,分別代表航空圖的點數 \(n\) 和邊數 \(v\)。
第 \(2\) 到第 \((n + 1)\) 行,每行一個字串,第 \((i + 1)\)
第 \((n + 2)\) 到第 \((n + v + 1)\) 行,每行兩個字串 \(x, y\)代表城市 \(x\) 和城市 \(y\) 之間存在一條直通航線。
輸出格式
本題存在 Special Judge。
請首先判斷是否存在滿足要求的路線,若存在,請給出一種旅行的方案。
如果存在路線,輸出格式為:
- 請在第一行輸出一個整數 \(m\),代表途徑最多的城市數。
- 在第 2 到第 \((m + 1)\) 行,每行一個字串,第 \((i + 1)\) 行的字串代表旅行路線第 \(i\)
否則請輸出一行一個字串 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;
}