「网络流 24 题」[18] 航空路线问题

特判与本地能过但是交上去就不对…。
同样是等会儿一块写吧。

题意

有n个城市,一些城市之间两两存在边。要求从城市1出发走到城市n然后再从城市n走到城市1。
要求经过的城市形成一个单调递增+单调递减序列。
求这最长的序列并且输出。

分析

首先考虑转化问题变成从1走到n两次。
然后求最大费用最大流。通过判断是否满流来判断是否存在解。

然后再通过正反各输出一次来输出方案。
大概就没了。
非要有什么注意的就是一开始怎么会想把边变成点呢 大概有点糊涂了orz

code

#include<bits/stdc++.h>
using namespace std;
/*
    某一条边正着或者反着只能流一次。
    给每一条边再来一个容量...?总之就是限制这条边只能流一次[???]
    把边拆成点。然后瞎搞。[????]
    还是有很多问题啊。
    源点和汇点总不可能是同一个吧。
    然后就是怎么瞎搞呢...???

    等等好像是我搞错了啊。
    是点不能重复两次经过所以是把点拆成边啊。
    哦 好像明朗了。
    那么就是最大费用流。

    然后节点的事情就把所有跟1连的点多连一个汇点。

    带环的话SPFA好像跑不动啊...
    那把环去掉行不行呢。

    从x到y再回x相当于x到y走两次
*/
void read(int &x){
    x=0; char c=getchar();
    for (;c<48;c=getchar());
    for (;c>47;c=getchar())x=(x<<1)+(x<<3)+(c^48);
}
string sx,sy,name[105];
#define M 205
struct ed{
    int x,cap,cost,nx;
}e[M*M];
#define inf 100000
int nx[M],ecnt;
void add(int x,int y,int cap,int cost){
    e[ecnt]=(ed){y,cap,cost,nx[x]};
    nx[x]=ecnt++;
    e[ecnt]=(ed){x,0,-cost,nx[y]};
    nx[y]=ecnt++;
}
int n;
struct EK_EK{
    int Q[M],dis[M],s,t,l,r,Flow,Cost,nxt[M];
    bool vis[M];
    bool spfa(int x){
        for (int i=s;i<=t;i++)dis[i]=-inf;
        l=r=0;
        Q[r++]=x;
        dis[x]=0;
        for (;l!=r;){
            x=Q[l++]; l%=M;
            vis[x]=0;
            for (int i=nx[x];~i;i=e[i].nx)if (e[i].cap>0&&e[i].cost+dis[x]>dis[e[i].x]){
                dis[e[i].x]=dis[x]+e[i].cost; nxt[e[i].x]=i;
                if (!vis[e[i].x]){
                    vis[e[i].x]=1;
                    Q[r++]=e[i].x; r%=M;
                }
            }
        }
        return dis[t]>-inf;
    }
    int dfs(int x,int f){
        if (x==s){
            Cost+=f*dis[t];
            return f;
        }
        int res=dfs(e[nxt[x]^1].x,min(f,e[nxt[x]].cap));
        e[nxt[x]].cap-=res; e[nxt[x]^1].cap+=res;
        return res;
    }   
    void solve(int ss,int tt){
        s=ss; t=tt;
        for (;spfa(s);)Flow+=dfs(t,inf);
    }
    void find1(int x) {
        cout<<name[x]<<endl;
        if(x==n) return;
        for(int i=nx[x+n];~i;i=e[i].nx)    if(e[i].cost==0&&e[i^1].cap!=0&&e[i].x<=n) 
            {e[i^1].cap=0,find1(e[i].x);return;}
    }
    void find2(int x) {
        if(x==n) return;
        for(int i=nx[x+n];~i;i=e[i].nx) if(e[i].cost==0&&e[i^1].cap!=0&&e[i].x<=n)
            {find2(e[i].x);break;}
        cout<<name[x]<<endl;
    }
    void pt(){
        find1(1);
        find2(1);
    }
}EK;
map< string , int >mp;
int main(){
//  freopen("LOJ6122.in","r",stdin);
    int m,x,y;
    read(n); read(m);
    memset(nx,-1,sizeof(nx));
    int s=0,t=2*n+1;
    add(s,1,2,0);
    for (int i=1;i<=n;i++){
        cin>>name[i];
        mp[name[i]]=i;
    }
    add(n+n,t,2,0);
    add(1,n+1,2,1);
    add(n,n+n,2,1);
    for (int i=2;i<n;i++){
        add(i,i+n,1,1);
    }
    for (int i=1;i<=m;i++){
        cin>>sx>>sy;
        x=mp[sx]; y=mp[sy];
        if (x>y)swap(x,y);
        add(x+n,y,1+(x==1&&y==n),0);
    }
    EK.solve(s,t);
    if (EK.Flow==2)printf("%d\n",EK.Cost-2),EK.pt();
    else printf("No Solution!\n");      
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值