航空路线问题

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

这道题方案数在8点的时候就写完了,然后打印方案我写了。。。一个半小时?
最后莫名其妙的调出来了。。

题意是让求一条经过城市最多的路线,除了起点每个城市经过一次。
题目一看就很像一个最大费用最大流啊。。城市之间拆点连边限制容量1,费用1。
但是。。之间的关系怎么办呢。还要走回去啊。。
(于是我就卡住了)
有一个思路,走回去不重复=走两遍不重复。那么题意就是要求两条不相交从起点到终点的路径使得费用最大。那么起点和终点的容量设置为2,其他为1.
跑一遍最大费用最大流。注意一下起点到终点的路径容量是可以为2的。

然后。。就是我打印了一个半小时的方案。。(感觉智商下线
dfs两遍想到了,然后回溯想到了。可方案一直不对是为什么啊……我没return在中途他走来走去了(因为限制路线是一条,dfs后直接return不让他遍历别的边)。
还是不对为什么啊….然后输出了半天过程意识到我这样dfs有可能会往上走(方向走反)。
所以加个限制条件不能这样走。

打印方案真麻烦=_=。下次一定要注意各种细节比如那些边不合法不能走。

#include<bits/stdc++.h>
using namespace std;

const int INF=1e9;
const int base=1e5;
const int MAXN=2e5+5;

struct edge{
    int u,to,next,w,c;
}e[MAXN<<1];

int head[MAXN],cnt=1;
inline void add(int u,int v,int w,int cost){e[++cnt]=(edge){u,v,head[u],w,cost},head[u]=cnt;}

int n,m,s,t;

queue<int>q;
map<string,int>ma;
map<int,string>ma2;

int pre[MAXN],dis[MAXN];
bool vis[MAXN];
bool SPFA(int x){
    memset(vis,0,sizeof(vis));
    memset(pre,0,sizeof(dis));
    for(int i=s;i<=t;i++)dis[i]=-INF;
    q.push(x);vis[x]=1;dis[x]=0;
    while(q.size()){
        int u=q.front();
        //cout<<u<<endl;
        q.pop();vis[u]=0;
        for(int i=head[u];i;i=e[i].next){
        //  cout<<e[i].u<<" "<<e[i].to<<"ok"<<endl;
            int v=e[i].to,w=e[i].c;
            if(e[i].w){
                if(dis[u]+w>dis[v]){
                    dis[v]=dis[u]+w;
                    pre[v]=i;
                    if(!vis[v]){
                        q.push(v);
                        vis[v]=1;
                    }
                }
            }
        }
    }
    if(dis[t]==-INF)return 0;
    return 1;
}

int MC(){
    int cost=0,flow=0;
    while(SPFA(s)){
        int tem=INF;
        for(int i=pre[t];i;i=pre[e[i].u]){
            tem=min(tem,e[i].w);
        }
        flow+=tem;
        for(int i=pre[t];i;i=pre[e[i].u]){
            e[i].w-=tem;e[i^1].w+=tem;
            cost+=e[i].c*tem;
        }
    }
    return flow==2?cost-2:0;
}

void dfs(int u,int fa){
    for(int i=head[u];i;i=e[i].next){
        int v=e[i].to;
        if(v==fa)continue;
        if(v>base&&v-base<u)continue;
        if(!e[i].w&&!vis[v]) {
            if(u<base)cout<<ma2[u]<<endl;
            if(v<base)vis[v]=1;
            dfs(v,u);
            return;
        }
    }
}

void dfs2(int u,int fa){
    for(int i=head[u];i;i=e[i].next){
        int v=e[i].to;
        if(v==fa)continue;
        if(v>base&&v-base<u)continue;
        if(!e[i].w&&!vis[v]) {
            dfs2(v,u);
            if(u<base)cout<<ma2[u]<<endl;
            return;
        }
    }
}

int main(){
    string tem1,tem2;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        cin>>tem1;
        ma[tem1]=i;
        ma2[i]=tem1;
        if(i==1||i==n)add(i,i+base,2,1),add(i+base,i,0,-1);
        else add(i,i+base,1,1),add(i+base,i,0,-1);
    }
    s=0,t=n+base+1;
    add(s,1,2,0);add(1,s,0,0);
    add(n+base,t,2,0);add(t,n+base,0,0);
    for(int i=1;i<=m;i++){
        cin>>tem1>>tem2;
        if((ma[tem1]==1&&ma[tem2]==n)||(ma[tem1]==n&&ma[tem2]==1)){
            if(ma[tem1]>ma[tem2])swap(tem1,tem2);
            add(ma[tem1]+base,ma[tem2],2,0),add(ma[tem2],ma[tem1]+base,0,0);
        }
        else{
            if(ma[tem1]<ma[tem2])add(ma[tem1]+base,ma[tem2],1,0),add(ma[tem2],ma[tem1]+base,0,0);
            else add(ma[tem2]+base,ma[tem1],1,0),add(ma[tem1],ma[tem2]+base,0,0);
        }
    }
    if(int ans=MC()){
        printf("%d\n",ans); 
        dfs(1,1);
        vis[1]=0;vis[n+base]=0;
        dfs2(1,1);
    }
    else printf("No Solution!\n");
    return 0;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值