LOJ6122 「网络流 24 题 - 18」 航空路线问题 最长不相交路径 坠大费用坠大流

大家都很强, 可与之共勉 。

题意:
  给定一张航空图,图中顶点代表城市,边代表两个城市间的直通航线。现要求找出一条满足下述限制条件的且途经城市最多的旅行路线。
  从最西端城市出发,单向从西向东途经若干城市到达最东端城市,然后再单向从东向西飞回起点(可途经若干城市)。除起点城市外,任何城市只能访问一次。对于给定的航空图,试设计一个算法找出一条满足要求的最佳航空旅行路线。

题解:
  网络流要做吐了
  题目可以转化为求出两条不相交的路,之中只有 1 号点和n号点可以经过两次,问最长路……
  因为不能点相交,所以拆点……
  然后小的向大的连边,因为只能是从西向东走。
  然后就判最大流是否为 2 ,不是2就没有解,然后费用就是最长路径的长度。
  输出方案时DFS找流过的边输出结果。

注意到会存在 1n 的边,这种情况特判处理,连流量为 2 <script type="math/tex" id="MathJax-Element-454">2</script>的弧。

  STL重度依赖患者

# include <bits/stdc++.h>

template < class T >  inline bool chkmax ( T& d, const T& x )  {   return ( d < x ) ? ( d = x ), 1 : 0 ;  }
template < class T >  inline bool chkmin ( T& d, const T& x )  {   return ( d > x ) ? ( d = x ), 1 : 0 ;  }

# define oo 0x3f3f3f3f

# define N 40040

std :: map < std :: string, int > hs ;
std :: map < int, std :: string > hs2 ;

class  MaxCostMaxFlow  {
    private :
        struct edge  {
            int to, nxt, w, cost ;
        } g [N << 1] ;

        int S, T ;
        int head [N], dis [N], pre [N], ecnt ;

        inline bool spfa ( int S, int T )  {
            static std :: bitset < N > inq ;
            static std :: deque < int > Q ;
            inq.reset ( ) ; Q.clear ( ) ;
            memset ( pre, 0, sizeof ( int ) * ( T + 1 ) ) ;
            memset ( dis, -1, sizeof ( int ) * ( T + 1 ) ) ;
            Q.push_front ( S ) ;
            inq [S] = 1 ;
            dis [S] = 0x3f3f3f3f ; // big enough !!!
            while ( ! Q.empty ( ) )  {
                int u = Q.front ( ) ; Q.pop_front ( ) ;
                inq [u] = 0 ;
                for ( int i = head [u] ; i ; i = g [i].nxt )  {
                    int& v = g [i].to ;
                    if ( g [i].w && chkmax ( dis [v], dis [u] + g [i].cost ) )  {
                        pre [v] = i ;
                        if ( ! inq [v] )  {
                            ( Q.empty ( ) || dis [v] > dis [Q.front ( )] ) ? Q.push_front ( v ) : Q.push_back ( v ) ;
                            inq [v] = 1 ;
                        }
                    }
                }
            }
            return ( bool ) pre [T] ;
        }
    public :
        MaxCostMaxFlow ( )  {  ecnt = 1 ; memset ( head, 0, sizeof head ) ;  }

        inline void clear ( )  {
            ecnt = 1 ; memset ( head, 0, sizeof head ) ;
        }

        inline void add_edge ( int u, int v, int w, int cost )  {
            g [++ ecnt] = ( edge )  {  v, head [u], w, cost } ; head [u] = ecnt ;
            g [++ ecnt] = ( edge )  {  u, head [v], 0, -cost } ; head [v] = ecnt ;
        }

        std :: pair < int, int > mcmf ( int S, int T )  {
            this -> S = S, this -> T = T ;
            int flow = 0, cost = 0, x ;
            while ( spfa ( S, T ) )  {
                x = oo ;
                for ( int i = pre [T] ; i ; i = pre [g [i ^ 1].to] )  chkmin ( x, g [i].w ) ;
                for ( int i = pre [T] ; i ; i = pre [g [i ^ 1].to] )  {
                    g [i].w -= x, g [i ^ 1].w += x ;
                    cost += x * g [i].cost ;
                }

                flow += x ;
            }
            return std :: make_pair ( flow, cost ) ;
        }

        bool vis [N] ;

        inline void Dfs ( const int& n, int u, bool fir )  {
            if ( u == n )  return ;
            if ( u < n )  {
                if ( fir )  std :: cout << hs2 [u] << '\n' ;
                Dfs ( n, u + n, fir ) ;
                if ( ! fir )  std :: cout << hs2 [u] << '\n' ;
                return ;
            }
            for ( int i = head [u] ; i ; i = g [i].nxt )  {
                int& v = g [i].to ;
                if ( ! vis [v] && g [i].w == 0 )  {
                    vis [v] = 1 ;
                    Dfs ( n, v, fir ) ;
                    return ;
                }
            }
        }

        inline void display ( int n, int S, int T )  {
            std :: pair < int, int > ans = mcmf ( S, T ) ;
            if ( ans.first < 2 )  {
                puts ( "No Solution!" ) ;
                return ;
            }
            printf ( "%d\n", ans.second - 2 ) ;
            Dfs ( n, 1, 1 ) ;
            std :: cout << hs2 [n] << '\n' ;
            Dfs ( n, 1, 0 ) ;
        }
} Lazer ;

int main ( )  {
    int n, m ;
    scanf ( "%d%d", & n, & m ) ;
    for ( int i = 1 ; i <= n ; ++ i )  {
        static std :: string ss ;
        std :: cin >> ss ;
        hs [ss] = i ;
        hs2 [i] = ss ;
    }
    const int S = n + n + 1, T = n + n + 2 ;

    for ( int i = 2 ; i < n ; ++ i )  {
        Lazer.add_edge ( i, i + n, 1, 1 ) ;
    }

    Lazer.add_edge ( 1, 1 + n, 2, 1 ) ;  Lazer.add_edge ( n, n + n, 2, 1 ) ;

    while ( m -- )  {
        static std :: string fr, to ;
        std :: cin >> fr >> to ;
        int u = hs [fr], v = hs [to] ;
        if ( u > v )  u ^= v ^= u ^= v ;
        if ( u == 1 && v == n )  Lazer.add_edge ( u + n, v, 2, 0 ) ;
        else Lazer.add_edge ( u + n, v, 1, 0 ) ;
    }

    Lazer.add_edge ( S, 1, oo, 0 ) ; Lazer.add_edge ( n + n, T, oo, 0 ) ;

    Lazer.display ( n, S, T ) ;
    return 0 ;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值