【网络流24题】航空路线问题

(网络流24题大多需要spj,所以需要一个有spj的oj,本系列代码均在www.oj.swust.edu.cn测试通过)
这道题我们首先要想明白的是飞机往返飞两次其实等价于两架飞机走一次,只要两架飞机的路线除出发点和结束点以外不重合即可。那么我们只要将每个点拆分成两个点,点与点之间连一条容量为1,费用为1(起始点和结束点的容量为2)的流即可,跑一边最大费用最大流就行了。个人觉得最难的还是输出路径的问题,我用的方法是重新检索图中的边查看哪些流过了再加一些特判输出,写的挺麻烦的,要是有更好的办法欢迎提出来。
对了,这题还有个坑,就是图中只有起始点和结束点的时候,这时路径即为起始点-结束点-起始点,特判掉就可以了。

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<iostream>
#include<iomanip>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<map>
using namespace std;
bool pd[100000];
bool ps[100000];
struct bian
{
    int l,r,f,v,lei;
}a[1000000];
int fir[1000000];
int nex[1000000];
int tot=1;
void add_edge(int l,int r,int f,int v,int lei=0)
{
    a[++tot].l=l;
    a[tot].r=r;
    a[tot].f=f;
    a[tot].v=v;
    a[tot].lei=lei;
    nex[tot]=fir[l];
    fir[l]=tot;
}
int s=0,t=999;
int fro[1000];
int juli[1000];
bool spfa()
{
    static int dui[1000000];
    memset(juli,0x8f,sizeof(juli));
    int top=1,my_final=2;
    dui[top]=s;
    pd[s]=true;
    juli[s]=0;
    while(top<my_final)
    {
        int u=dui[top++];
        for(int o=fir[u];o;o=nex[o])
        {
            if(juli[a[o].r]<juli[u]+a[o].v && a[o].f)
            {
                juli[a[o].r]=juli[u]+a[o].v;
                fro[a[o].r]=o;
                if(!pd[a[o].r]) pd[a[o].r]=true,dui[my_final++]=a[o].r;
            }
        }
        pd[u]=false;
    }
    if(juli[t]==0x8f8f8f8f) return false;
    return true;
}
int add_flow()
{
    int o=t;
    int temp=2147483647;
    while(o!=s)
    {
        temp=min(temp,a[fro[o]].f);
        o=a[fro[o]^1].r;
    }
    o=t;
    while(o!=s)
    {
        a[fro[o]].f-=temp;
        a[fro[o]^1].f+=temp;
        o=a[fro[o]^1].r;
    }
    return temp;
}
string ss[101];
map<string,int> mapp1;
map<int,string> mapp2;
string mid;
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        if(i!=1 && i!=n)
        {
            add_edge(2*i,2*i+1,1,1,1);
            add_edge(2*i+1,2*i,0,-1);
        }
        else
        {
            add_edge(2*i,2*i+1,2,1,1);
            add_edge(2*i+1,2*i,0,-1);
        }
        cin>>ss[i];
        mapp1[ss[i]]=i;
        mapp2[i]=ss[i];
    }
    add_edge(s,2,2,0,1);
    add_edge(2,s,0,0);
    add_edge(2*n+1,t,2,0,1);
    add_edge(t,2*n+1,0,0);
    bool spj=false;
    for(int i=1;i<=m;i++)
    {
        cin>>mid;
        int x=mapp1[mid];
        cin>>mid;
        int y=mapp1[mid];
        add_edge(2*x+1,2*y,1,0,1);
        add_edge(2*y,2*x+1,0,0);
        if((x==1 && y==n) || (x==n && y==1)) spj=true;
    }
    int ans=0;
    int cost=0;
    while(spfa())
    {
        int k=add_flow();
        cost+=juli[t]*k;
        ans+=k;
    }
    if(ans!=2)
    {
        if(spj) cout<<2<<endl<<ss[1]<<endl<<ss[n]<<endl<<ss[1];
        else cout<<"No Solution!";
    }
    else
    {
        cout<<cost-2<<endl;
        int k=s;
        while(k!=t)
        {
            for(int o=fir[k];o;o=nex[o])
                if(a[o].lei && a[o].f==0)
                {
                    k=a[o].r;
                    if(!ps[a[o].r>>1] && mapp2[a[o].r>>1].size()!=0) cout<<mapp2[a[o].r>>1]<<endl;
                    ps[a[o].r>>1]=true;
                    if(a[o^1].f!=2) pd[o]=true;
                    break;
                }
        }
        while(k!=s)
        {
            for(int o=fir[k];o;o=nex[o])
            {
                if(a[o^1].lei && !a[o^1].f && !pd[o^1])
                {
                    k=a[o].r;
                    if(!ps[a[o].r>>1] && mapp2[a[o].r>>1].size()!=0) cout<<mapp2[a[o].r>>1]<<endl;
                    ps[a[o].r>>1]=true;
                    break;
                }
            }
        }
        cout<<ss[1]<<endl;
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值