您的位置:首页 > 理论基础 > 计算机网络

[题解] [网络流二十四题(十一)] 航空路线问题(最大费用最大流)

2017-12-28 17:04 507 查看

11.航空路线问题

题目描述 Description

给定一张航空图,图中顶点代表城市,边代表2城市间的直通航线。现要求找出一条满足下述限制条件的且途经城市最多的旅行路线

(1)从最西端城市出发,单向从西向东途经若干城市到达最东端城市,然后再单向从东向西飞回起点(可途经若干城市)

(2)除起点城市外,任何城市只能访问1次

输入描述 Input Description

第1行有2个正整数N和V,N表示城市数,N<100,V表示直飞航线数。接下来的N行中每一行是一个城市名,可乘飞机访问这些城市。城市名出现的顺序是从西向东。也就是说,设i,j是城市表列中城市出现的顺序,当i>j时,表示城市i在城市j的东边,而且不会有2个城市在同一条经线上。城市名是一个长度不超过15的字符串,串中的字符可以是字母或阿拉伯数字。例如,AGR34或BEL4。再接下来的V行中,每行有2个城市名,中间用空格隔开,如 city1 city2 表示city1到city2 有一条直通航线,从city2到city1也有一条直通航线。

输出描述 Output Description

将最佳航空旅行路线输出,第1行是旅行路线中所访问的城市总数M。接下来的M+1行是旅行路线的城市名,每行写1个城市名。首先是出发城市名,然后按访问顺序列出其它城市名。注意,最后1行(终点城市)的城市名必然是出发城市名。如果问题无解,则输出”No Solution!”

样例输入 Sample Input

8 9

Vancouver

Yellowknife

Edmonton

Calgary

Winnipeg

Toronto

Montreal

Halifax

Vancouver Edmonton

Vancouver Calgary

Calgary Winnipeg

Winnipeg Toronto

Toronto Halifax

Montreal Halifax

Edmonton Montreal

Edmonton Yellowknife

Edmonton Calgary

样例输出 Sample Output

7

Vancouver

Edmonton

Montreal

Halifax

Toronto

Winnipeg

Calgary

Vancover

分析: 从西飞向东,再从东边飞回起点,就相当于两条航线从最西边出发到达最东端,所以我们直接将s与最西端的地点相连,将最东端的点与t相连。怎么保证每个点除起点外只经过一次呢,我们将每个地点拆成Xi和Yi,连一条容量为1的边。因为我们流法的特殊性,最西端的点与最东端的点都需要将其连一条容量为2的边。再来考虑怎么让流经的城市最多:将两个城市之间的航线设置花费为1,求最大费用最大流即可

建图: 将每个点拆成Xi和Yi,最西端和最东端的点Xi和Yi之间连一条容量为2的边,其他点Xi和Yi之间连一条容量为1的边。s向Xwest连一条容量为INF的边,Yeast向t连一条容量为INF的边。从西至东若两个城市联通,则从西边城市向东边城市连一条容量为1,花费为1的边

若流量为2,则输出最大费用,若流量为1,则说明只有从最西端直接飞向最东端一种方案,所以输出2,若流量为0,输出”No Solution!”

#include <bits/stdc++.h>
using namespace std;
#define INF 2100000000
map <string,int> m;
queue <int> q;
string a,b,x[500];
struct node {
int to;
int flow;
int cost;
int next;
}e[50000];
int n,v,s,t,tot=1;
bool pd[500];
int head[500];
int pre[500];
int dis[500];
int pe[500];
int ans[500];
bool Q;
int read() {
int ans=0,flag=1;
char ch=getchar();
while( (ch>'9' || ch<'0') && ch!='-' ) ch=getchar();
if(ch=='-') flag=-1,ch=getchar();
while(ch>='0' && ch<='9') ans=ans*10+ch-'0',ch=getchar();
return ans*flag;
}
void addedge(int u,int v,int flow,int cost) {
e[++tot].to=v;
e[tot].flow=flow;
e[tot].cost=cost;
e[tot].next=head[u];
head[u]=tot;
e[++tot].to=u;
e[tot].flow=0;
e[tot].cost=-cost;
e[tot].next=head[v];
head[v]=tot;
return ;
}
bool spfa() {
memset(pre,-1,sizeof(pre));
memset(dis,-1,sizeof(dis));
dis[s]=0;
pd[s]=1;
while(!q.empty()) q.pop();
q.push(s);
while(!q.empty()) {
int now=q.front();
q.pop();
pd[now]=0;
for(int i=head[now];i;i=e[i].next) {
if(dis[e[i].to]<dis[now]+e[i].cost && e[i].flow>0) {
dis[e[i].to]=dis[now]+e[i].cost;
pre[e[i].to]=now;
pe[e[i].to]=i;
if(!pd[e[i].to]) {
q.push(e[i].to);
pd[e[i].to]=1;
}
}
}
}
return pre[t]!=-1;
}
void search() {
memset(pd,0,sizeof(pd));
int now=1;
cout<<endl<<x[now];
now+=n;
do{
for(int i=head[now];i;i=e[i].next) {
if(e[i].to!=s && e[i].to!=t && e[i^1].flow!=0 && !pd[e[i].to]) {
now=e[i].to;
cout<<endl<<x[now];
pd[now]=1;
now+=n;
break;
}
}
}while(now!=n+n);
now=1+n;
pd
=0;
ans[++ans[0]]=1;
do{
for(int i=head[now];i;i=e[i].next) {
if(e[i].to!=s && e[i].to!=t && e[i^1].flow!=0 && !pd[e[i].to]) {
now=e[i].to;
ans[++ans[0]]=now;
pd[now]=1;
now+=n;
break;
}
}
}while(now!=n+n);
for(int i=ans[0]-1;i>=1;i--)
cout<<endl<<x[ans[i]];
return ;
}
void Mincost() {
int cost=0;
int flow=0;
while(spfa()) {
int f=INF;
for(int i=t;i!=s;i=pre[i])
f=min(f,e[pe[i]].flow);
flow+=f;
for(int i=t;i!=s;i=pre[i]) {
cost+=f*e[pe[i]].cost;
e[pe[i]].flow-=f;
e[pe[i]^1].flow+=f;
}
}
if(flow==2) {
printf("%d",cost);
search();
}
else if(Q) {
printf("2");
cout<<endl<<x[1];
cout<<endl<<x
;
cout<<endl<<x[1];
}
else
printf("No Solution!");
return ;
}
int main() {
n=read(),v=read();
s=0;t=n*2+1;
for(int i=1;i<=n;i++)
cin>>x[i],m[x[i]]=i;
for(int i=1;i<=v;i++) {
cin>>a>>b;
if((m[a]==1 && m[b]==n) || (m[a]==n && m[b]==1)) Q=1;
addedge(m[a]+n,m[b],1,1);
}
for(int i=1;i<=n;i++)
if(i==1 || i==n)
addedge(i,i+n,2,0);
else
addedge(i,i+n,1,0);
addedge(s,1,INF,0);
addedge(n+n,t,INF,0);
Mincost();
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐