LOJ6010 「網絡流 24 題 - 11」數字梯形 墜大費用墜大流 墜大權不相交路徑

大家都很強, 可與之共勉 。

題意:
  給定一個由 n 行數字組成的數字梯形如下圖所示。梯形的第一行有m 個數字。從梯形的頂部的m 個數字開始,在每個數字處可以沿左下或右下方向移動,形成一條從梯形的頂至底的路徑。
  分別遵守以下規則:

  • 從梯形的頂至底的m 條路徑互不相交;
  • 從梯形的頂至底的m 條路徑僅在數字結點處相交;
  • 從梯形的頂至底的m 條路徑允許在數字結點相交或邊相交。

      要求輸出三個任務的的最大值。

題解:
  墜大費用墜大流(這名字好迷
  第一個任務 TASK1:
  老生常談的每個點只經過一次 拆點建立分層圖。即每個點a 拆爲a1,a2
  建圖:

建立超級源點S ,和匯點TS 向第一層的點分別連一條(Sa1) 的弧(其實(Sa2) 也是茲磁的),容量爲1,0(a2v) 。最後一層的點向匯點T 連邊,a2 連向T 容量爲爲1 ,費用爲0
每一個點aa1a2 連邊,權值爲v 。對於a 走到ba2b1 連一條容量爲爲1 ,費用爲0 的邊。
如此一來一定保證了每個點只被經過一次,一定有m 條不相交不重合的路徑。

  對於最大權值,直接跑最大費用最大流就可以了。

  第二個任務 TASK2:

  每個點不要求只經過一次了,但是每條邊只能經過一次。
  我們考慮如何在上面的方法改進。

  方法一:不拆點,限制邊的流量爲1
  方法二:拆點,把a1a2 的流量設爲+a2b1 的連邊流量爲1 ,把最後一層的元素到匯點T 的流量設爲+

  啦啦啦啦

  第三個任務 TASK3:

  亂跑就好了
  我們考慮如何在上面的方法改進。

  方法一:不拆點,限制邊的流量爲+
  方法二:拆點,把a1a2 的流量設爲+a2b1 的連邊流量爲+ ,把最後一層的元素到匯點T 的流量設爲+

  然後我調了一陣子發現時建圖邊界GG了,注意S的dis初值要足夠大

# 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 5010
# define M 16010

class  MaxCostMaxFlow  {
    private :
        struct edge  {
            int to, nxt, w, cost ;
        } g [M << 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 ) ;
        }
} Lazer ;

int n, m ;
int a [50] [50] ;
int id1 [50] [50], id2 [50] [50] ;

void Solve1 ( int S, int T )  {
    Lazer.clear ( ) ;
    for ( int i = 1 ; i <= m ; ++ i )   Lazer.add_edge ( S, id1 [1] [i], 1, 0 ) ;
    for ( int i = 1 ; i <= m + n - 1 ; ++ i )  Lazer.add_edge ( id2 [n] [i], T, 1, 0 ) ;
    for ( int i = 1 ; i <= n ; ++ i )
        for ( int j = 1 ; j <= i + m - 1 ; ++ j )  {
            Lazer.add_edge ( id1 [i] [j], id2 [i] [j], 1, a [i] [j] ) ;
            int x = i + 1, y = j + 1 ;
            if ( x <= n && y <= i + m - 1 + 1 )  Lazer.add_edge ( id2 [i] [j], id1 [x] [y], 1, 0 ) ;
            x = i + 1, y = j ;
            if ( x <= n && y <= i + m - 1 + 1 )  Lazer.add_edge ( id2 [i] [j], id1 [x] [y], 1, 0 ) ;
        }
    printf ( "%d\n", Lazer.mcmf ( S, T ).second ) ;
}

void Solve2 ( int S, int T )  {
    Lazer.clear ( ) ;
    for ( int i = 1 ; i <= m ; ++ i )   Lazer.add_edge ( S, id1 [1] [i], 1, a [1] [i] ) ;
    for ( int i = 1 ; i <= m + n - 1 ; ++ i )  Lazer.add_edge ( id1 [n] [i], T, oo, 0 ) ;
    for ( int i = 1 ; i <= n ; ++ i )
        for ( int j = 1 ; j <= i + m - 1 ; ++ j )  {
            int x = i + 1, y = j + 1 ;
            if ( x <= n && y <= i + m - 1 + 1 )  Lazer.add_edge ( id1 [i] [j], id1 [x] [y], 1, a [x] [y] ) ;
            x = i + 1, y = j ;
            if ( x <= n && y <= i + m - 1 + 1 )  Lazer.add_edge ( id1 [i] [j], id1 [x] [y], 1, a [x] [y] ) ;
        }
    printf ( "%d\n", Lazer.mcmf ( S, T ).second ) ;
}

void Solve3 ( int S, int T )  {
    Lazer.clear ( ) ;
    for ( int i = 1 ; i <= m ; ++ i )   Lazer.add_edge ( S, id1 [1] [i], 1, a [1] [i] ) ;
    for ( int i = 1 ; i <= m + n - 1 ; ++ i )  Lazer.add_edge ( id1 [n] [i], T, oo, 0 ) ;
    for ( int i = 1 ; i <= n ; ++ i )
        for ( int j = 1 ; j <= i + m - 1 ; ++ j )  {
            int x = i + 1, y = j + 1 ;
            if ( x <= n && y <= i + m - 1 + 1 )  Lazer.add_edge ( id1 [i] [j], id1 [x] [y], oo, a [x] [y] ) ;
            x = i + 1, y = j ;
            if ( x <= n && y <= i + m - 1 + 1 )  Lazer.add_edge ( id1 [i] [j], id1 [x] [y], oo, a [x] [y] ) ;
        }
    printf ( "%d\n", Lazer.mcmf ( S, T ).second ) ;
}

int main ( )  {

//  freopen ( "in.txt", "r", stdin ) ;

    int cnt ( 0 ) ;
    scanf ( "%d%d", & m, & n ) ;
    for ( int i = 1 ; i <= n ; ++ i )
        for ( int j = 1 ; j <= i + m - 1 ; ++ j )  {
            scanf ( "%d", a [i] + j ) ;
            id1 [i] [j] = ++ cnt ;
            id2 [i] [j] = ++ cnt ;
        }
    const int S = cnt + 1, T = cnt + 2 ;

    Solve1 ( S, T ) ;
    Solve2 ( S, T ) ;
    Solve3 ( S, T ) ;
    return 0 ;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章