题目描述
给定一张航空图,图中顶点代表城市,边代表2城市间的直通航线。现要求找出一条满 足下述限制条件的且途经城市最多的旅行路线。
(1)从最西端城市出发,单向从西向东途经若干城市到达最东端城市,然后再单向从东 向西飞回起点(可途经若干城市)。
(2)除起点城市外,任何城市只能访问1次。
对于给定的航空图,试设计一个算法找出一条满足要求的最佳航空旅行路线。
输入
由文件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.txt 中。
文件第1 行是旅行路 线中所访问的城市总数M。
// 接下来的M+1 行是旅行路线的城市名,每行写1 个城市名。
// 首先是出发城市名,然后按访问顺序列出其它城市名。
//注意,最后1行(终点城市)的城市名 必然是出发城市名。
如果问题无解,则输出“No Solution!”。
样例输入
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
样例输出
7
题解
- 根据题意,首先我们要找出题目的限制。就是每个城市做多只能经过一次(起点可以经过两次,我们把终点也看成可以经过两次)。可以用容量为1(两次为2)的边来限制,题目又要求经过最多城市,我们可以求一个最大费用流(插入费用时变负数,按照最小费用的过程来做,答案再取反)。建模比较容易,只需要将每个点拆成两个点,一个代表进入城市,一个代表离开城市,显然,城市之间的边容量可以为inf,花费为0(已经有S->i和i->T的边限制)。
具体代码:
#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>
#include <map>
#define _(d) while(d (((ch=getchar())>47)&&ch<58))
using namespace std;
const int MaxN=223,Maxe=MaxN*MaxN/2;
const int inf=0x7fffffff;
int N,V,ans,h[MaxN],pre[MaxN],vst[MaxN],d[MaxN];
int S,T,tt,from[Maxe],to[Maxe],flow[Maxe],cst[Maxe],nxt[Maxe];
string str[MaxN];
map<string,int> hash;
inline void Get(int &x){char ch;_(!);x=ch-48;_()x=(x<<3)+(x<<1)+ch-48;}
inline void addedge(int u,int v,int w,int cc)
{
from[tt]=u;to[tt]=v;flow[tt]=w;cst[tt]=cc;
nxt[tt]=h[u];h[u]=tt++;
}
inline void add(int u,int v,int w,int cc)
{
addedge(u,v,w,cc);addedge(v,u,0,-cc);
}
queue<int> q;
bool spfa()
{
for(int i=0;i<MaxN;i++) pre[i]=-1,vst[i]=0,d[i]=inf;
q.push(S);vst[S]=1;d[S]=0;
while(!q.empty())
{
int u=q.front();q.pop();
vst[u]=0;
for(int i=h[u];i!=-1;i=nxt[i])
{
int v=to[i];
if(flow[i]&&d[v]>d[u]+cst[i])
{
d[v]=d[u]+cst[i];
pre[v]=i;
if(!vst[v])
{
q.push(v);
vst[v]=1;
}
}
}
}
return d[T]!=inf;
}
inline void work()
{
int ft;
while(spfa())
{
ft=inf;
for(int i=T;i!=S;i=from[pre[i]]){ft=min(ft,flow[pre[i]]);}
for(int i=T;i!=S;i=from[pre[i]])
{
int k=pre[i];
flow[k]-=ft;
flow[k^1]+=ft;
ans+=cst[k]*ft;
}
}
return;
}
int main()
{
for(int i=0;i<MaxN;i++) h[i]=-1;
string str1,str2;
Get(N);Get(V);
S=1;T=2*N;
add(S,S+N,2,-1);add(N,T,2,-1);
for(int i=1;i<=N;i++)
{
cin>>str[i];
hash[str[i]]=i;
if(i>1&&i<N) add(i,i+N,1,-1);
}
for(int i=1;i<=V;i++)
{
cin>>str1>>str2;
int a=hash[str1],b=hash[str2];
if(a>b) swap(a,b);
add(a+N,b,inf,0);
}
work();
if(flow[0]) printf("No Solution!\n");
else printf("%d\n",-2-ans);
return 0;
}