Description
给定一张航空图,图中顶点代表城市,边代表2城市间的直通航线。现要求找出一条满足下述限制条件的且途经城市最多的旅行路线。
(1)从最西端城市出发,单向从西向东途经若干城市到达最东端城市,然后再单向从东向西飞回起点(可途经若干城市)。
(2)除起点城市外,任何城市只能访问1次。
编程任务:
对于给定的航空图,试设计一个算法找出一条满足要求的最佳航空旅行路线。
由于本OJ无Special Judge , 所以只需要输出最多经过的城市数
Input
第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
程序运行结束时,将最佳航空旅行路线输出。
/*
第1 行是旅行路线中所访问的城市总数M。
接下来的M+1 行是旅行路线的城市名,每行写1 个城市名。首
先是出发城市名,然后按访问顺序列出其它城市名。注意,最后1行(终点城市)的城市名必然是出发城市名。如果问题无解,则输出“No Solution!”。
*/
一行,包含一个整数或字符串,表示最多经过的城市数,如果无解,则输出"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
Vancouver
*/
这个题,网上有很多的题解和解释啦,我还是希望提出自己的想法。
首先,分类是:最大费用最大流
求最大费用,我使用kuangbin模板,把所有的费用值取成负数,然后调用最小费用最大流的模板,求得结果之后对cost值取相反数
这个题运用的建模思想是:最长不相交路径
因为要求的是从1到n的两条不相交的路径
相当于最大流已经求出来了(必须是2,如果不是2,那么无解)
那么,我们在添加边的时候,需要拆点,把每个点拆成(ia)(ib)
除了1和n的任意点,添加边(ia,ib,1,-1)代表容量为1,单位费用为1
1和n添加边(ia,ib,2,-1)代表容量为2,单位费用为1
然后,可以直接到的边:(边输入边处理)
从小值的点添加边到大值的点:添加边(i+n,j,1,0)
但是如果i=1,j=n,那么需要添加的是(i+n,j,2,0)
代表是拆边之后的转移了
每条航线都是自西向东,本题可以转化为求航线图中从1到N两条不相交的路径,
使得路径长度之和最大。转化为网络流模型,就是找两条最长的增广路。
由于每个城市只能访问一次,要把城市拆成两个点,之间连接一条容量为1的边,费用设为1。
因为要找两条路径,所以起始点和终点内部的边容量要设为2。
那么费用流值-2就是两条路径长度之和,为什么减2,因为有两条容量为2的边多算了1的费用。
求最大费用最大流后,如果(<1.a>,<1.b>)不是满流,
那么我们找到的路径不够2条(可能是1条,也可能0条),所以无解
如何求转移的路径?!
这个问题想了很久,发现可以延续前几个题的思路,用深搜
因为这个题的生成路径必然是个环,我们可以从1点开始用深搜,如果遇到的点是拆点之前的ia,那么需要输出,继续搜索;如果遇到的ib,那么不能输出,继续搜索
这样搜索一圈,一定是从起点走到了终点,然后到了起点的前一个点(因为起点已经访问过了,所以必然会退出搜索)
然后在搜索外面再次输出一次起点就好
题意很好理解:但是想要转换模型是很难的:求最长两条不相交路径,用最大费用最大流解决
因为:题目中说了:流量一定是2
那么,我们的费用越大,说明走过的路径越长
把第i个城市拆分成两个顶点<i.a>,<i.b>。
1、对于每个城市i,连接(<i.a>,<i.b>)一条容量为1,费用为1的有向边,特殊地(<1.a>,<1.b>)和(<N.a>,<N.b>)容量设为2。
2、如果城市i,j(j>i)之间有航线,从<i.b>到<j.a>连接一条容量为1,费用为0的有向边。
求源<1.a>到汇<N.b>的最大费用最大流。如果(<1.a>,<1.b>)不是满流,那么无解。否则存在解,即为最大费用最大流量 - 2
对于,如何找最优的路径输出其实是很简单的
写一个dfs就好了啊
因为每个点的流量最大为2,也就是说最多一进一出
那么从起点开始往下搜索就好,如果碰到的节点编号<=n,那么就输出(拆点前的X集合的点)
否则不输出,继续搜索,这样dfs到最后一个点之后,肯定是回到起点了的
代码如下:
#include<bits/stdc++.h>
using namespace std;
const int maxn=10000;
const int maxm=300000;
const int inf=0x3f3f3f3f;
struct Edge{
int to,nxt,cap,flow,cost;
}edge[maxm];
char str[maxn][50],st[50];
int Head[maxn],tol;
int pre[maxn];
int dis[maxn];
int n,m,s,t,tot;
bool vis[maxn];
void init(){
tol=0;
memset(Head,-1,sizeof(Head));
}
void addedge(int u,int v,int cap,int cost){
edge[tol].to=v;
edge[tol].cap=cap;
edge[tol].cost=cost;
edge[tol].flow=0;
edge[tol].nxt=Head[u];
Head[u]=tol++;
edge[tol].to=u;
edge[tol].cap=0;
edge[tol].cost=-cost;
edge[tol].flow=0;
edge[tol].nxt=Head[v];
Head[v]=tol++;
}
bool spfa(int s,int t){
queue<int> q;
for(int i=0;i<=tot+1;i++){
dis[i]=inf;
vis[i]=false;
pre[i]=-1;
}
dis[s]=0;
vis[s]=true;
q.push(s);
while(!q.empty()){
int u=q.front();
q.pop();
vis[u]=false;
for(int i=Head[u];i!=-1;i=edge[i].nxt){
int v=edge[i].to;
if (edge[i].cap>edge[i].flow&&dis[v]>dis[u]+edge[i].cost){
dis[v]=dis[u]+edge[i].cost;
pre[v]=i;
if (!vis[v]){
vis[v]=true;
q.push(v);
}
}
}
}
if (pre[t]==-1) return false;
return true;
}
int minCostMaxflow(int s,int t,int &cost){
int flow=0;
cost=0;
while(spfa(s,t)){
int Min=inf;
for(int i=pre[t];i!=-1;i=pre[edge[i^1].to])
if (Min>edge[i].cap-edge[i].flow)
Min=edge[i].cap-edge[i].flow;
for(int i=pre[t];i!=-1;i=pre[edge[i^1].to]){
edge[i].flow+=Min;
edge[i^1].flow-=Min;
cost+=edge[i].cost*Min;
}
flow+=Min;
}
return flow;
}
void dfs(int u){
if (u<=n&&!vis[u]) cout<<str[u]<<endl;
vis[u]=true;
for(int i=Head[u];i!=-1;i=edge[i].nxt){
int v=edge[i].to;
if (edge[i].flow&&!vis[v]) dfs(v);
}
return;
}
int main(){
//freopen("input.txt","r",stdin);
while(scanf("%d%d",&n,&m)!=EOF){
init();
for(int i=1;i<=n;i++) scanf("%s",str[i]);
for(int i=2;i<n;i++) addedge(i,i+n,1,-1);
addedge(1,1+n,2,-1);
addedge(n,n+n,2,-1);
s=1;
t=n+n;
tot=n+n;
for(int i=1;i<=m;i++){
int pos1,pos2;
scanf("%s",st);
for(int j=1;j<=n;j++)
if (strcmp(str[j],st)==0){
pos1=j;
break;
}
scanf("%s",st);
for(int j=1;j<=n;j++)
if (strcmp(str[j],st)==0){
pos2=j;
break;
}
if (pos1>pos2) swap(pos1,pos2);
addedge(pos1+n,pos2,1,0);
if (pos1==1&&pos2==n) addedge(n+1,n,2,0);
}
int Cost,Maxflow;
Maxflow=minCostMaxflow(s,t,Cost);
if (Maxflow<2){
//printf("Maxflow: %d\n",Maxflow);
puts("No Solution!");
}
else{
printf("%d\n",-Cost-2);
memset(vis,false,sizeof(vis));
dfs(1);
cout<<str[1]<<endl;
}
}
return 0;
}