[题解] [网络流二十四题(十一)] 航空路线问题(最大费用最大流)

11.航空路线问题

题目描述 Description
给定一张航空图,图中顶点代表城市,边代表2城市间的直通航线。现要求找出一条满足下述限制条件的且途经城市最多的旅行路线
(1)从最西端城市出发,单向从西向东途经若干城市到达最东端城市,然后再单向从东向西飞回起点(可途经若干城市)
(2)除起点城市外,任何城市只能访问1次
输入描述 Input Description
第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 Description
将最佳航空旅行路线输出,第1行是旅行路线中所访问的城市总数M。接下来的M+1行是旅行路线的城市名,每行写1个城市名。首先是出发城市名,然后按访问顺序列出其它城市名。注意,最后1行(终点城市)的城市名必然是出发城市名。如果问题无解,则输出”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
Vancover

分析: 从西飞向东,再从东边飞回起点,就相当于两条航线从最西边出发到达最东端,所以我们直接将s与最西端的地点相连,将最东端的点与t相连。怎么保证每个点除起点外只经过一次呢,我们将每个地点拆成 Xi Yi ,连一条容量为1的边。因为我们流法的特殊性,最西端的点与最东端的点都需要将其连一条容量为2的边。再来考虑怎么让流经的城市最多:将两个城市之间的航线设置花费为1,求最大费用最大流即可
建图: 将每个点拆成 Xi Yi ,最西端和最东端的点 Xi Yi 之间连一条容量为2的边,其他点 Xi Yi 之间连一条容量为1的边。s向 Xwest 连一条容量为INF的边, Yeast 向t连一条容量为INF的边。从西至东若两个城市联通,则从西边城市向东边城市连一条容量为1,花费为1的边
若流量为2,则输出最大费用,若流量为1,则说明只有从最西端直接飞向最东端一种方案,所以输出2,若流量为0,输出”No Solution!”

#include <bits/stdc++.h>
using namespace std;
#define INF 2100000000
map <string,int> m;
queue <int> q;
string a,b,x[500];
struct node {
    int to;
    int flow;
    int cost;
    int next;
}e[50000];
int n,v,s,t,tot=1;
bool pd[500];
int head[500];
int pre[500];
int dis[500];
int pe[500];
int ans[500];
bool Q;
int read() {
    int ans=0,flag=1;
    char ch=getchar();
    while( (ch>'9' || ch<'0') && ch!='-' ) ch=getchar();
    if(ch=='-') flag=-1,ch=getchar();
    while(ch>='0' && ch<='9') ans=ans*10+ch-'0',ch=getchar();
    return ans*flag;
}
void addedge(int u,int v,int flow,int cost) {
    e[++tot].to=v;
    e[tot].flow=flow;
    e[tot].cost=cost;
    e[tot].next=head[u];
    head[u]=tot;
    e[++tot].to=u;
    e[tot].flow=0;
    e[tot].cost=-cost;
    e[tot].next=head[v];
    head[v]=tot;
    return ;
}
bool spfa() {
    memset(pre,-1,sizeof(pre));
    memset(dis,-1,sizeof(dis));
    dis[s]=0;
    pd[s]=1;
    while(!q.empty()) q.pop();
    q.push(s);
    while(!q.empty()) {
        int now=q.front();
        q.pop();
        pd[now]=0;
        for(int i=head[now];i;i=e[i].next) {
            if(dis[e[i].to]<dis[now]+e[i].cost && e[i].flow>0) {
                dis[e[i].to]=dis[now]+e[i].cost;
                pre[e[i].to]=now;
                pe[e[i].to]=i;
                if(!pd[e[i].to]) {
                    q.push(e[i].to);
                    pd[e[i].to]=1;
                }
            }
        }
    }
    return pre[t]!=-1;
}
void search() {
    memset(pd,0,sizeof(pd));
    int now=1;
    cout<<endl<<x[now];
    now+=n;
    do{
        for(int i=head[now];i;i=e[i].next) {
            if(e[i].to!=s && e[i].to!=t && e[i^1].flow!=0 && !pd[e[i].to]) {
                now=e[i].to;
                cout<<endl<<x[now];
                pd[now]=1;
                now+=n;
                break;
            }
        }
    }while(now!=n+n);
    now=1+n;
    pd[n]=0;
    ans[++ans[0]]=1;
    do{
        for(int i=head[now];i;i=e[i].next) {
            if(e[i].to!=s && e[i].to!=t && e[i^1].flow!=0 && !pd[e[i].to]) {
                now=e[i].to;
                ans[++ans[0]]=now;
                pd[now]=1;
                now+=n;
                break;
            }
        }
    }while(now!=n+n);
    for(int i=ans[0]-1;i>=1;i--)
        cout<<endl<<x[ans[i]];
    return ;
}
void Mincost() {
    int cost=0;
    int flow=0;
    while(spfa()) {
        int f=INF;
        for(int i=t;i!=s;i=pre[i])
            f=min(f,e[pe[i]].flow);
        flow+=f;
        for(int i=t;i!=s;i=pre[i]) {
            cost+=f*e[pe[i]].cost;
            e[pe[i]].flow-=f;
            e[pe[i]^1].flow+=f;
        }
    }
    if(flow==2) {
        printf("%d",cost);
        search();
    }
    else if(Q) {
        printf("2");
        cout<<endl<<x[1];
        cout<<endl<<x[n];
        cout<<endl<<x[1];
    }
    else
        printf("No Solution!");
    return ;
}
int main() {
    n=read(),v=read();
    s=0;t=n*2+1;
    for(int i=1;i<=n;i++)
        cin>>x[i],m[x[i]]=i;
    for(int i=1;i<=v;i++) {
        cin>>a>>b;
        if((m[a]==1 && m[b]==n) || (m[a]==n && m[b]==1)) Q=1;
        addedge(m[a]+n,m[b],1,1);
    }
    for(int i=1;i<=n;i++)
        if(i==1 || i==n)
            addedge(i,i+n,2,0);
        else
            addedge(i,i+n,1,0);
    addedge(s,1,INF,0);
    addedge(n+n,t,INF,0);
    Mincost();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值