题目:洛谷P2770。
题目大意:
给定一张航空图,图中顶点代表城市,边代表 2 城市间的直通航线。现要求找出一条满足下述限制条件的且途经城市最多的旅行路线。
(1)从最西端城市出发,单向从西向东途经若干城市到达最东端城市,然后再单向从东向西飞回起点(可途经若干城市)。
(2)除起点城市外,任何城市只能访问 1 次。
对于给定的航空图,试设计一个算法找出一条满足要求的最佳航空旅行路线。
解题思路:题目要求找两条不重复的路径,且距离最长。
找路径不就是跑最大流吗?
那还要距离最远?最大费用最大流即可。
首先对于某个点拆成入点和出点,然后入点与出点连接容量为1,费用为0的边。
对于每两个相连的城市,从出发的出点向到达的入点连接容量为1,费用为1的边。
由于起点和终点要经过两次,所以这两个点拆出的入点向出点连接的边容量为2。
还有一个细节,如果有1到n的边,是可以直接往返的,所以有这种边,容量也为2。
最后从起点的入点到终点的出点跑最大费用最大流。
如果流量不到2,则无解。
否则费用就是旅行路线经过的城市数量。
找路径,直接从起点开始dfs,找有流量经过的边,记录下来即可。
C++ Code:
#include<iostream>
#include<map>
#include<string>
#include<string.h>
#include<queue>
#include<vector>
#include<cstdio>
using namespace std;
map<string,int>p;
vector<int>v;
queue<int>q;
int n,m,cnt=-1,head[606],d[606],a[606],pre_e[606];
bool vis[606];
string s[105];
struct edge{
int from,to,cap,dis,nxt,flow;
}e[605<<3];
inline void addedge(int from,int to,int cap,int dist){
e[++cnt]=(edge){from,to,cap,dist,head[from],cap};
head[from]=cnt;
e[++cnt]=(edge){to,from,0,-dist,head[to],0};
head[to]=cnt;
}
bool spfa(int s,int t,int& flow,int& cost){
memset(a,0x3f,sizeof a);
memset(pre_e,-1,sizeof a);
memset(vis,0,sizeof vis);
for(int i=0;i<=605;++i)d[i]=-2000000;
d[s]=0;
vis[s]=1;
q.push(s);
while(!q.empty()){
int u=q.front();
q.pop();
vis[u]=0;
for(int i=head[u];i!=-1;i=e[i].nxt)
if(e[i].cap&&d[e[i].to]<d[u]+e[i].dis){
d[e[i].to]=d[u]+e[i].dis;
pre_e[e[i].to]=i;
a[e[i].to]=(a[u]>e[i].cap)?e[i].cap:a[u];
if(!vis[e[i].to]){
vis[e[i].to]=1;
q.push(e[i].to);
}
}
}
if(d[t]<=-2000000)return 0;
flow+=a[t];
cost+=d[t]*a[t];
for(int i=t;i!=s;i=e[pre_e[i]].from){
e[pre_e[i]].cap-=a[t];
e[pre_e[i]^1].cap+=a[t];
}
return 1;
}
int maxflow(int s,int t,int& cost){
int flow=0;
cost=0;
while(spfa(s,t,flow,cost));
return flow;
}
void find(int now){
if(now<=n)v.push_back(now);
for(int i=head[now];i!=-1;i=e[i].nxt)
if(e[i].cap<e[i].flow&&e[i].flow){
find(e[i].to);
++e[i].cap;
break;
}
}
int main(){
ios::sync_with_stdio(0);
cin>>n>>m;
memset(head,-1,sizeof head);
for(int i=1;i<=n;++i){
cin>>s[i];
p[s[i]]=i;
}
for(int i=2;i<n;++i)addedge(i,i+n,1,0);
addedge(1,n+1,2,0);addedge(n,n<<1,2,0);
for(;m--;){
string u,v;
cin>>u>>v;
int a=p[u],b=p[v];
if(a>b)a^=b^=a^=b;
if(a==1&&b==n)addedge(n+1,n,2,1);else
addedge(a+n,b,1,1);
}
int cost;
if(maxflow(1,n<<1,cost)!=2)return!printf("No Solution!");
cout<<cost<<endl;
find(1);
for(int i=0,sz=v.size();i<sz;++i)cout<<s[v[i]]<<endl;
v.clear();
find(1);
for(int i=v.size()-2;i>=0;--i)cout<<s[v[i]]<<endl;
return 0;
}