链接:
https://vjudge.net/problem/计蒜客-A1429
这个题的题意很长,概括一下要点:
有若干个机场,它们的名字仅有英文字母组成,且不超过10位。这些机场一定包含"Xian","Qingdao","Pudong","Hongqiao"。其中"Pudong"和"Hongqiao"之间有一条双向的免费铁路,你可以在这两个机场之间任意穿梭。有若干双向航线,双向飞行的费用一致,你只能沿着航线飞或者坐火车。
你从"Xian"出发,到达"Pudong"或"Hongqiao"(这个或是逻辑or)。然后再飞到"Qingdao",之后到"Pudong",最后坐飞机出国,问直到出国前最少花费多少?
注意务必按照顺序旅行。
你不能在一个机场起飞超过1次,也不能在一个机场降落超过1次,但这不意味着你不能经过一个机场2次。
你如果在"Pudong","Hongqiao"以外的机场,你还没有到达最终目的地"Pudong",你也不能坐火车,所以只能继续起飞。而如果你又回到了这个机场,你还需要再起飞,这样就超过1次起飞次数了。所以"Pudong","Hongqiao"以外的机场只能经过一次。
"Pudong","Hongqiao"比较特殊,它们中间有免费铁路相连,而且"Pudong"是不允许在出国前起飞的,因为你还要从这里起飞出国。
显然,在"Pudong"和"Hongqiao"之间反复横跳没有意义,我们不会连续的在两个机场间折返。
你可以从"Xian"出发,到达"Hongqiao",然后飞到"Qingdao",最后飞到"Pudong"。
你可以从"Xian"出发,到达"Hongqiao",然后坐火车到"Pudong",然后飞"Qingdao",但是你不能在"Pudong"起飞,所以这个路线有问题。
你可以从"Xian"出发,到达"Pudong",然后飞"Qingdao",但是你不能在"Pudong"起飞,所以这个路线有问题。。
你可以从"Xian"出发,到达"Pudong",然后坐火车去"Hongqiao",然后飞到"Qingdao",之后飞到"Hongqiao",然后坐火车去"Pudong"。
所以综上,只有2条路线:
Xian-Hongqiao-Qingdao-Pudong
Xian-Pudong-Hongqiao-Qingdao-Hongqiao-Pudong
这个题不能用最短路做,因为最短路很难限制你路径上点的顺序。
这路要介绍一个思想:
假如题目给定了一个无向图,要并求你从A出发,经过B,到达C,且每个点只能经过一次。你可以把这个A-B-C的路径拆分,认为是A-B,B-C两条路径。又因为是无向图,所以又可以认为是A-B,C-B两条路径。你可以从源点向A,C引1单位流量,从B点向汇点引出2单位流量,并且把每个点的流量(除了端点A,B,C)限制为1,不设边容量的限制,边的费用设置为边权,跑一个最小费用最大流即可。假如最终流量是2,那么就有解,解就是费用,否则无解。流可以直观的想象成路径,如果最终流量是2,那么从A-B,C-B必然存在不相交的路径。
但是如果题目给定了一个无向图,要并求你从A出发,经过B,再经过C,最终到达D,且每个点只能经过一次,这就比较难搞了。
如果还用路径拆分的办法,把A-B-C-D分成A-B,C-B,C-D,然后从源点向A引1单位流量,从源点向C引2单位流量,从B点向汇点引出2单位流量,从D点向汇点引出1单位流量。这样,如果总流量是3,有可能是A-B,C-B,C-D这样的3条路径,也有可能是A-D,C-B,C-B这样的3条路径,那很显然你不好区分这两种情况。
但是看回题目,你的路线是Xian-Hongqiao-Qingdao-Pudong或Xian-Pudong-Hongqiao-Qingdao-Hongqiao-Pudong,计费的路径简写为A-B-C-D或A-D,B-C,C-B
这意味着你不需要区分这两种路径的区别。
除了上面的4个机场,每个机场拆成ar(到达)和de(起飞)两个点,从ar->de连一条容量为1费用为0的边。航线x-y需要从x的de到y的ar连边,从y的de到x的ar离岸边,费用都是边权。从源点向"Xian"连一条容量为1的边,从源点向"Qingdao"连一条容量为2的边,从"Hongqiao"向汇点连一条容量为2的边,从"Pudong"向汇点连一条容量为1的边。跑最小费用最大流,若流量为3,则费用就是解,反之无解。
在输入时,给的都是字符串,你可以用string存储,并用map<string,int>给字符串编号,这样方便连边。建议把"Xian","Hongqiao","Qingdao","Pudong"先映射为1,2,3,4,方便处理。不用map映射也可以排序再二分查找或者建trie树查找,但是比较麻烦。
由于边数m<=10000,流量是3,考虑一般出题人不会刻意卡spfa,跑一次最小费用最大流的时间开销不会超过1e6这个数量级,一共有160组样例,4s应该能跑过。
代码如下:
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<cstring>
#include<map>
using namespace std;
const int MAXN=21000,MAXM=61000,inf=1000000000;
struct edge
{
int y,next,rev,c,cost;
}graph[MAXM];
struct pii
{
int cost,flow;
};
pii mp(int cost,int flow)
{
pii re;
re.cost=cost;
re.flow=flow;
return re;
}
int head[MAXN];
int add(int num,int x,int y,int c,int cost)
{
// printf("%d %d %d %d
",x,y,c,cost);
int e=num*2-1,rev=num*2;
graph[e].y=y;
graph[e].c=c;
graph[e].cost=cost;
graph[e].rev=rev;
graph[e].next=head[x];
head[x]=e;
graph[rev].y=x;
graph[rev].c=0;
graph[rev].cost=-cost;
graph[rev].rev=e;
graph[rev].next=head[y];
head[y]=rev;
}
int pre_v[MAXN],pre_e[MAXN];
int dis[MAXN],cnt_qu[MAXN];
bool in_qu[MAXN];
bool cmp(int x,int y,bool minn)//minn==1,<;minn==0,>
{
if(minn)return x<y;
return x>y;
}
queue<int> q;
void push(int x)
{
cnt_qu[x]++;
in_qu[x]=1;
q.push(x);
}
int pop()
{
int x=q.front();
in_qu[x]=0;
q.pop();
return x;
}
bool spfa(int s,int t,int all,bool minn)
{
int init=inf;
if(minn==0)init=-inf;
for(int i=1;i<=all;i++)dis[i]=init;
for(int i=1;i<=all;i++)in_qu[i]=0;
for(int i=1;i<=all;i++)cnt_qu[i]=0;
while(!q.empty())q.pop();
dis[s]=0;
push(s);
while(!q.empty())
{
int now=pop();
//printf("%d %d %d
",now,dis[now],head[now]);
for(int k=head[now];k!=-1;k=graph[k].next)
{
int y=graph[k].y;
//printf("%d ",y);
if(graph[k].c>0)
{
if(cmp(dis[now]+graph[k].cost,dis[y],minn))
{
dis[y]=dis[now]+graph[k].cost;
pre_e[y]=k;
pre_v[y]=now;
if(!in_qu[y])push(y);
if(cnt_qu[y]>=all)
{
return 0;
}
}
}
}
}
return dis[t]!=init;
}
pii min_cost_flow(int s,int t,int all,bool minn)
{
int cost=0,flow=0;
while(spfa(s,t,all,minn))
{
int now=t;
int f=inf;
while(now!=s)
{
int e=pre_e[now];
f=min(f,graph[e].c);
now=pre_v[now];
}
flow+=f;
now=t;
while(now!=s)
{
int e=pre_e[now];
int rev=graph[e].rev;
graph[e].c-=f;
graph[rev].c+=f;
now=pre_v[now];
}
cost+=f*dis[t];
}
return mp(cost,flow);
}
map<string,int> name;
int de(int x)
{
return x*2;
}
int ar(int x)
{
return x*2-1;
}
int cnt_e=0;
int solve(int n)
{
int S=2*n+1,T=2*n+2;
for(int i=5;i<=n;i++)
{
add(++cnt_e,ar(i),de(i),1,0);
}
add(++cnt_e,ar(1),de(1),2,0);
add(++cnt_e,S,ar(1),2,0);
add(++cnt_e,ar(2),de(2),1,0);
add(++cnt_e,S,ar(2),1,0);
add(++cnt_e,ar(3),de(3),2,0);
add(++cnt_e,de(3),T,2,0);
add(++cnt_e,ar(4),de(4),1,0);
add(++cnt_e,de(4),T,1,0);
pii ans=min_cost_flow(S,T,T,1);
if(ans.flow<3)return -1;
return ans.cost;
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
int num=0;
cnt_e=0;
name.clear();
int n;
scanf("%d",&n);
memset(head,-1,sizeof(head));
name["Hongqiao"]=1;
name["Pudong"]=2;
name["Qingdao"]=3;
name["Xian"]=4;
num=4;
for(int i=1;i<=n;i++)
{
string str1,str2;
int cost;
cin>>str1>>str2>>cost;
if(name[str1]==0)name[str1]=++num;
if(name[str2]==0)name[str2]=++num;
add(++cnt_e,de(name[str1]),ar(name[str2]),inf,cost);
add(++cnt_e,de(name[str2]),ar(name[str1]),inf,cost);
}
int ans=solve(num);
printf("%d
",ans);
}
}