题解:P17102 [ICPC 2017 Qingdao R] Our Journey of Xian Ends
lailai0916 · · 题解
题意简述
行程从西安出发,先到上海参加婚礼,再到青岛,最后返回上海浦东。除规定的上海换乘外,每个机场最多到达一次且最多离开一次。求全部航班的最小费用。
解题思路
先把完整行程拆成西安到上海、上海到青岛、青岛到上海三段。航线都是双向的,所以可以反转第二段。三段路便拥有统一的方向:一条从西安前往上海,两条从青岛前往上海。
不使用补偿换乘时,第一次经过上海必须在虹桥到达并从虹桥离开,最后在浦东到达。反转第二段后,三条路径分别为:
- 西安到虹桥。
- 青岛到虹桥。
- 青岛到浦东。
使用补偿换乘时,第一次从浦东到达上海,再由虹桥离开;第二次从虹桥到达,再前往浦东。反转第二段后,三条路径分别为:
- 西安到浦东。
- 两条青岛到虹桥的路径。
因此,两种情况的起点多重集合都是「西安、青岛、青岛」。终点多重集合都是「虹桥、虹桥、浦东」。西安对应的流最终到达虹桥或浦东,恰好决定是否使用补偿,不会产生第三种行程。
用最小费用最大流同时选择这
对每条双向航线,分别从一个机场的出点向另一个机场的入点连边。边的费用等于机票价格,容量取
任意合法行程反转第二段后,都会在网络中形成
初始残量网络的正向边费用均非负。每次增广后,用势能把残量边重标为非负费用。然后用 Dijkstra 求下一条最短增广路。总流量仅为
参考代码
#include <bits/stdc++.h>
using namespace std;
using pii=pair<int,int>;
const int N=40015;
const int M=10005;
const int E=80025;
const int inf=0x3f3f3f3f;
struct Edge
{
int v,w,c;
}e[E];
int dis[N],h[N],pre[N],cnt,tot,last;
int x[M],y[M],co[M];
vector<int> G[N];
map<string,int> mp;
int get(const string &s)
{
if(!mp.count(s))mp[s]=tot++;
return mp[s];
}
void add(int u,int v,int w,int c)
{
e[cnt]={v,w,c};
G[u].push_back(cnt++);
e[cnt]={u,0,-c};
G[v].push_back(cnt++);
}
bool dijkstra(int s,int t,int n)
{
fill(dis,dis+n,inf);
priority_queue<pii,vector<pii>,greater<pii>> q;
dis[s]=0;
q.push({0,s});
while(!q.empty())
{
auto [d,u]=q.top();
q.pop();
if(d!=dis[u])continue;
for(auto i:G[u])
{
int v=e[i].v,nd=d+e[i].c+h[u]-h[v];
if(e[i].w&&dis[v]>nd)
{
dis[v]=nd;
pre[v]=i;
q.push({nd,v});
}
}
}
if(dis[t]==inf)return 0;
for(int i=0;i<n;i++)if(dis[i]!=inf)h[i]+=dis[i];
return 1;
}
pii flow(int s,int t,int n)
{
fill(h,h+n,0);
int f=0,ans=0;
while(f<3&&dijkstra(s,t,n))
{
int d=3-f,cost=0,u=t;
while(u!=s)
{
d=min(d,e[pre[u]].w);
cost+=e[pre[u]].c;
u=e[pre[u]^1].v;
}
u=t;
while(u!=s)
{
e[pre[u]].w-=d;
e[pre[u]^1].w+=d;
u=e[pre[u]^1].v;
}
f+=d;
ans+=d*cost;
}
return {f,ans};
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T;
cin>>T;
while(T--)
{
for(int i=0;i<last;i++)G[i].clear();
mp.clear();
tot=0;
int m;
cin>>m;
for(int i=1;i<=m;i++)
{
string a,b;
cin>>a>>b>>co[i];
x[i]=get(a);
y[i]=get(b);
}
int xa=get("Xian"),qd=get("Qingdao"),hq=get("Hongqiao"),pd=get("Pudong"),s=tot*2,t=s+1;
last=t+1;
cnt=0;
for(int i=1;i<=m;i++)
{
add(x[i]+tot,y[i],3,co[i]);
add(y[i]+tot,x[i],3,co[i]);
}
for(int i=0;i<tot;i++)add(i,i+tot,i==qd||i==hq?2:1,0);
add(s,xa,1,0);
add(s,qd,2,0);
add(hq+tot,t,2,0);
add(pd+tot,t,1,0);
pii ans=flow(s,t,last);
cout<<(ans.first==3?ans.second:-1)<<'\n';
}
return 0;
}