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

网络流与线性规划24题11航空路线问题

2013-04-07 20:39 429 查看
问题描述:

给定一张航空图,图中顶点代表城市,边代表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 7

Vancouver Vancouver

Yellowknife Edmonton

Edmonton Montreal

Calgary Halifax

Winnipeg Toronto

Toronto Winnipeg

Montreal Calgary

Halifax Vancouver

Vancouver Edmonton

Vancouver Calgary

Calgary Winnipeg

Winnipeg Toronto

Toronto Halifax

Montreal Halifax

Edmonton Montreal

Edmonton Yellowknife

Edmonton Calgary

分析:

这题建网络流模型比较简单,就是求出两条最长不相交的增广路。最大费用最大流可解,除了起点和终点每个点容量为1,起点终点容量为2,这样就可以限制每个点只能取一次。点的容量如何为1和2呢,之前有说过,把一个点拆成2个点,2点之间加边,权值为1或者2即可。对于每条航线,只要增加从西到东的航线就行了,因为给出2个城市不一定是从西向东的顺序,因此要判断一下,航线容量为1,费用为0。值得注意的是起点和终点城市容量为2,在极端情况下只有2个城市a,b,如果容量为1就错了,因为可以这样的情况:a->b->a。求一次最大费用流就可以了。起点是1,终点是n。因为求出的是最大流,所以保证在可能的情况下流量一定为2,如果流量为1或者0,则No
Solution!。

可以转化为最小费用流,方法很简单就是把费用从正变为负,这样求出的最小费用取负就是最大费用流。求增广路还是用spfa。

有人会疑惑,不是从西到东再到西么。换个角度想就是2条从西向东的路,起点终点相同。

不过打印路线还是比较麻烦的,用一个visit数组判断是否打印过这个点,因为一共有2条路线。具体的看代码实现吧。

代码:

#include<cstdio>
#include<vector>
#include<queue>
#include<cstring>
using namespace std;
const int maxn = 1001;
const int INF = 1<<30;
struct edge{
int from,to,cap,flow,cost;
edge(int a,int b,int c,int d,int e):from(a),to(b),cap(c),flow(d),cost(e){}
};
int s,t;
int n,m;
bool inqueue[maxn];
int p[maxn],d[maxn],a[maxn];
char city[maxn>>1][16];
bool visit[maxn];
vector<int> g[maxn];
vector<edge> edges;
void addedge(int from,int to,int cap,int cost)
{
edges.push_back(edge(from,to,cap,0,cost));
edges.push_back(edge(to,from,0,0,-cost));
int k=edges.size();
g[from].push_back(k-2);
g[to].push_back(k-1);
}
int geti(char *s)
{
int i=1;
while(i<=n && strcmp(s,city[i]))
i++;
return i;
}
bool spfa(int &flow,int &cost)
{
memset(inqueue,false,sizeof(inqueue));
for(int i=s;i<=t;i++) d[i]=INF;
d[s]=0;inqueue[s]=true;a[s]=INF;p[s]=0;
queue<int> q;
q.push(s);
while(!q.empty()){
int x=q.front();q.pop();inqueue[x]=false;
for(int i=0;i<g[x].size();i++){
edge &e=edges[g[x][i]];
if(e.cap>e.flow && d[e.to]>d[x]+e.cost){
d[e.to]=d[x]+e.cost;
p[e.to]=g[x][i];
a[e.to]=min(a[x],e.cap-e.flow);
if(!inqueue[e.to]){
q.push(e.to);
inqueue[e.to]=true;
}
}
}
}
if(d[t]==INF) return false;

flow+=a[t];
cost+=a[t]*d[t];
int x=t;
while(x!=s){
edges[p[x]].flow+=a[t];
edges[p[x]^1].flow-=a[t];
x=edges[p[x]].from;
}
return true;
}
int mincost()
{
int flow=0,cost=0;
while(spfa(flow,cost));
return cost;
}
void printans(int cost)
{
printf("%d\n",cost);
int x=1+n;
printf("%s\n",city[1]);
while(x!=t){
for(int i=0;i<g[x].size();i++){
edge &e=edges[g[x][i]];
if(e.flow==1&&!visit[e.to]){
visit[e.to]=true;
printf("%s\n",city[e.to]);
x=e.to+n;
break;
}
}
}
x=n;
while(x!=s){
for(int i=0;i<g[x].size();i++){
edge &e=edges[g[x][i]^1];
if(e.flow==1 && !visit[e.from-n]){
x=e.from-n;
visit[x]=true;
printf("%s\n",city[x]);
break;
}
}
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
if(i==1||i==n) addedge(i,i+n,2,-1);
else addedge(i,i+n,1,-1);
}
for(int i=1;i<=n;i++)
scanf("%s",city[i]);

char from[16],to[16];
for(int i=1;i<=m;i++){
scanf("%s%s",from,to);
int f=geti(from),o=geti(to);
int m=min(f,o);
int k=o+f-m;
if(m==1&&k==n) addedge(m+n,k,2,0);
else addedge(m+n,k,1,0);
}
s=1;t=n+n;
int cost=-2-mincost();
if(edges[g[s][0]].flow!=2) printf("No Solution!\n");
else  printans(cost);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: