LOJ6003 「網絡流 24 題 - 4」 魔術球 墜小路徑覆蓋數 二分圖墜大匹配

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

題意:
  假設有n 根柱子,現要按下述規則在這n 根柱子中依次放入編號爲 1,2,3,4, 的球。
  每次只能在某根柱子的最上面放球。
  在同一根柱子中,任何2 個相鄰球的編號之和爲完全平方數。
  試設計一個算法,計算出在n 根柱子上最多能放多少個球。

題解:

  轉化爲最小路徑覆蓋,即每一根柱子當成一條路。枚舉解(或者二分答案),然後按照其和爲平方數原則建邊,從小數向大數連邊。

  注意到輸出方案時需要重新建圖……

# include <bits/stdc++.h>

# define N 5010

class Network  {
private :
    struct edge  {
        int to, w, nxt ;
        edge ( ) {        }
        edge ( int to, int w, int nxt ) : to ( to ), w ( w ), nxt ( nxt ) {        }  
    } g [60010 << 1] ;

    int head [N], cur [N], ecnt ;
    int S, T , dep [N] ;
    int to [N] ;

    inline int dfs ( int u, int a )  {
        if ( u == T || ! a )  return a ;
        int flow = 0, v, f ;
        for ( int& i = cur [u] ; i ; i = g [i].nxt )  {
            v = g [i].to ;
            if ( dep [v] == dep [u] + 1 )  {
                f = dfs ( v, std :: min ( g [i].w, a - flow ) ) ;
                g [i].w -= f, g [i ^ 1].w += f ;
                to [u] = v ;
                flow += f ;
                if ( a == flow )  return a ;
            }
        }
        if ( ! flow )  dep [u] = -1 ;
        return flow ;
    }

    inline bool bfs ( int S, int T )  {
        static std :: queue < int > q ;
        memset ( dep, 0, sizeof ( int ) * ( T + 1 ) ) ;
        dep [S] = 1 ;
        q.push ( S ) ;
        while ( ! q.empty ( ) )  {
            int u = q.front ( ) ;  q.pop ( ) ;
            for ( int i = head [u] ; i ; i = g [i].nxt )  {
                int& v = g [i].to ;
                if ( g [i].w &&  ! dep [v] )  {
                    dep [v] = dep [u] + 1 ;
                    q.push ( v ) ;
                }
            }
        }
        return dep [T] ;
    }
public :
    Network ( )  {    ecnt = 1 ; }

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

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

    }
    inline int dinic ( int S, int T )  {
        this -> S = S, this -> T = T ;
        static int rt = 0 ; // static ;
        while ( bfs ( S, T ) )    {
            memcpy ( cur, head, sizeof ( int ) * ( T + 1 ) ) ; 
            rt += dfs ( S, 0x3f3f3f3f ) ;
        }
        return rt ;
    }

    void display ( int n, int S, int T )  {

        clear ( ) ;
        for ( int i = 1 ; i <= n ; ++ i )  {
            add_edge ( S, i, 1 ) ;
            add_edge ( i + 2500, T, 1 ) ;
            for ( int j = 1 ; j < i ; ++ j )  {
                if ( sqrt ( i + j ) == floor ( sqrt ( i + j ) ) )  {
                add_edge ( j, i + 2500, 1 ) ;
            }
            }
        }
        dinic ( S, T ) ;
        static std :: bitset < N > vis ;
        vis.reset ( ) ;
        for ( int i = 1 ; i <= n ; ++ i )
            if ( ! vis.test ( i ) )  {
                int curr = i ;
                do  {
                    vis.set ( curr ) ;
                    printf ( "%d ", curr ) ;
                    curr = to [curr] - 2500 ;
                } while ( curr != -2500 ) ;
                puts ( "" ) ;
            }
    }
} Lazer ;

int main ( )  {
    int n ;
    scanf ( "%d", & n ) ;

    const int S = 5000, T = 5001 ;

    for ( int i = 1 ; i ; ++ i )  {
        for ( int j = 1 ; j < i ; ++ j )
            if ( sqrt ( i + j ) == floor ( sqrt ( i + j ) ) )  {
                Lazer.add_edge ( j, i + 2500, 1 ) ;
            }
        Lazer.add_edge ( S, i, 1 ) ;
        Lazer.add_edge ( i + 2500, T, 1 ) ;
        if ( i - Lazer.dinic ( S, T ) > n )  {
            printf ( "%d\n", i - 1 ) ;
            Lazer.display ( i - 1, S, T ) ;
            break ;
        }
    }
    return 0 ;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章