网络流24题之十一 航空路线问题
填坑
Description
给定一张航空图,图中顶点代表城市,边代表2城市间的直通航线。现要求找出一条满 足下述限制条件的且途经城市最多的旅行路线。 (1)从最西端城市出发,单向从西向东途经若干城市到达最东端城市,然后再单向从东 向西飞回起点(可途经若干城市)。 (2)除起点城市外,任何城市只能访问1次。 编程任务: 对于给定的航空图,试设计一个算法找出一条满足要求的最佳航空旅行路线。
Input
由文件input.txt提供输入数据。文件第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
程序运行结束时,将最佳航空旅行路线输出到文件output.txt 中。文件第1 行是旅行路 线中所访问的城市总数M。接下来的M+1 行是旅行路线的城市名,每行写1 个城市名。首 先是出发城市名,然后按访问顺序列出其它城市名。注意,最后1行(终点城市)的城市名 必然是出发城市名。如果问题无解,则输出“No Solution!”。
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
Output
7
Vancouver
Edmonton
Montreal
Halifax
Toronto
Winnipeg
Calgary
Vancouver
<
div class=”content pre”>
模型
求最长两条不相交路径,用最大费用最大流解决。
实现
为了限制经过次数,将每个点i拆成xi,yi.
1、从xi向yi连一条容量为1,费用为1的有向边(1<i<N),
2、从x1向y1连一条容量为2,费用为1的有向边,
3、从xN向yN连一条容量为2,费用为1的有向边,
4、如果存在边(i,j)(i<j)从yi向xj连一条容量为1,费用为0的有向边.
求x1到yN的最大费用最大流.若(x1,y1)满流,则有解,答案为最大费用最大流-2;否则,无解.
分析
每条航线都是自西向东,本题可以转化为求航线图中从1到N两条不相交的路径,使得路径长度之和最大。转化为网络流模型,就是找两条最长的增广路。由于每个城市只能访问一次,要把城市拆成两个点,之间连接一条容量为1的边,费用设为1。因为要找两条路径,所以起始点和终点内部的边容量要设为2。那么费用流值-2就是两条路径长度之和,为什么减2,因为有两条容量为2的边多算了1的费用。求最大费用最大流后,如果(<1.a>,<1.b>)不是满流,那么我们找到的路径不够2条(可能是1条,也可能0条),所以无解。
代码:
#include <cstdio> #include <algorithm> #include <queue> #include <map> #include <string> #include <cstring> #include <iostream> #define M 10000 #define N 150 #define INF 0x33333333 #define min(x,y) ((x<y)?(x):(y)) using namespace std; typedef pair<int,int> Pair; map <string,int>ma; struct node{int from,to,next,flow,cost;}e[M]; int tot=-1,st[M]; int n,m,x,y,z; int pe[N],pv[N],dis[N],vis[N]; void add(int x,int y,int z,int zz){ e[++tot].to=y; e[tot].from=x; e[tot].flow=z; e[tot].cost=zz; e[tot].next=st[x]; st[x]=tot; } void add_edge(int x,int y,int z,int zz){add(x,y,z,zz),add(y,x,0,-zz);} queue<int>que; bool spfa(int S,int T) { memset(dis,0,sizeof dis); memset(vis,0,sizeof vis); que.push(S),vis[S]=1,dis[S]=0; while(!que.empty()) { int now=que.front();que.pop();vis[now]=0; for (int i=st[now];~i;i=e[i].next) if (e[i].flow>0 && dis[e[i].to]<dis[now]+e[i].cost) { dis[e[i].to]=dis[now]+e[i].cost; pe[e[i].to]=i,pv[e[i].to]=now; if (!vis[e[i].to]) vis[e[i].to]=1,que.push(e[i].to); } } return dis[T]; } int mfmc(int S,int T,int ans_flow) { int COST=0,flow; while(ans_flow) { if (!spfa(S,T)) return -1; flow=ans_flow; for (int i=T;i!=S;i=pv[i]) flow=min(flow,e[pe[i]].flow); COST+=flow*dis[T]; ans_flow-=flow; for (int i=T;i!=S;i=pv[i]) e[pe[i]].flow-=flow,e[pe[i]^1].flow+=flow; } return COST; } //为了限制经过次数,将每个点i拆成xi,yi. // //从xi向yi连一条容量为1,费用为1的有向边(1<i<N), //从x1向y1连一条容量为2,费用为1的有向边, //从xN向yN连一条容量为2,费用为1的有向边, //如果存在边(i,j)(i<j)从yi向xj连一条容量为1,费用为0的有向边. // //求x1到yN的最大费用最大流.若(x1,y1)满流,则有解,答案为最大费用最大流-2;否则,无解. string res[N]; int v[N]; int main() { int m,from,to,cnt=0; scanf("%d%d",&n,&m); memset(e,-1,sizeof e); memset(st,-1,sizeof st); string s,s1; for (int i=1;i<=n;i++) cin>>s,ma[s]=i,res[i]=s; for(int i=0;i<m;i++) { cin>>s>>s1; int l=ma[s],l1=ma[s1]; if (l>l1) swap(l,l1); if (l==1&&l1==n) add_edge(l+n,l1,2,0); else add_edge(l+n,l1,1,0); } add_edge(1,1+n,2,1); add_edge(n,n+n,2,1); for (int i=2;i<n;i++) add_edge(i,i+n,1,1); int S=1,T=n+n; int ans=mfmc(S,T,2); // printf("%d %dn",ans.first,ans.second); if (ans==-1) { puts("No Solution!"); return 0; } printf("%dn",ans-2); cout<<res[1]<<endl; for (int i=st[S+n],now,j;~i;i=e[i].next) if (!e[i].flow&&!(i&1)) { now=e[i].to; while(now) { cout<<res[now]<<endl; v[now]=1; for (j=st[now+n],now=0;~j;j=e[j].next) if (!e[j].flow&&!(j&1)) { now=e[j].to;break; } } break; } for (int i=st[T-n],now,j;~i;i=e[i].next) if (!e[i^1].flow&&(i&1)&&!v[e[i].to-n]) { now=e[i].to-n; while(now) { cout<<res[now]<<endl; v[now]=1; for (j=st[now],now=0;~j;j=e[j].next) if (!e[j^1].flow&&(j&1)) { now=e[j].to-n; break; } } break; } }