0w1

GCJ 2009 R2 pC. Stock Charts ( Bipartite Matching, Hungarian or Dinic )

Dashboard - Round 2 2009 - Google Code Jam
Let us first represent the relations of the charts by directing edges from chart A to chart B iff A can be laid under B on the same sheet of paper ( as the description says ) without interfering one another. It will form a DAG. We want to maxmize the number of ordered pairs ( u, v ), where there is an edge from u to v, and u does not belong to any other ordered pair as the first element nor does v belong to any other ordered pair as the second element. Thus it can be modelled as a bipartite problem, the objective then is to have maximum bipartite matching. This can be solved by Hungarian's, or max-flow algorithms such as Dinic's. Adding an extra source s and extra sink t, having s directed towards each node that directs out, and t being directed from each node that is directed in, the bipartite matching problem is equivalent to computing max flow of s to t.

// Hungarian
struct Hungarian{
    vvi G;
    vi bf;
    vi gf;
    int girl_size;
    vi vis;

    Hungarian( const vvi &_G, const int gs ): G( _G ), girl_size( gs ){
        bf.resize( girl_size );
        gf.resize( G.size(), -2 );
        vis.resize( girl_size );
        fill( bf.begin(), bf.end(), -1 );
    }

    bool foundMate( int u, const vvi &G ){
        for( int v : G[ u ] ){ // u: boy, v: girl
            if( vis[ v ] ) continue;
            vis[ v ] = 1;
            if( bf[ v ] == -1 || foundMate( bf[ v ], G ) ){
                bf[ v ] = u;
                gf[ u ] = v;
                return true;
            }
        }
        return false;
    }

    int maxMatch(){
        int res = 0;
        for( int i = 0; i < G.size(); ++i ){
            fill( vis.begin(), vis.end(), 0 );
            if( foundMate( i, G ) ) ++res;
        }
        return res;
    }
};

void solve(){
    int N, K; cin >> N >> K;
    vvi stock( N, vi( K ) );
    for( int i = 0; i < N; ++i )
        for( int j = 0; j < K; ++j )
            cin >> stock[ i ][ j ];
    vvi G( N );
    for( int i = 0; i < N; ++i )
        for( int j = 0; j < N; ++j ) if( i != j ){
            bool ng = false;
            for( int k = 0; k < K; ++k )
                if( not ( stock[ i ][ k ] < stock[ j ][ k ] ) )
                    ng = true;
            if( not ng ) G[ i ].push_back( j );
        }
    Hungarian hung( G, N );
    cout << N - hung.maxMatch() << "\n";
}
// Dinic
struct Dinic{
    int n, m, s, t;
    vector< Nedge > edge;
    vvi G;
    vb vis;
    vi dis;
    vi cur;
    Dinic( int sz ){
        n = sz;
        edge.clear();
        G.clear(); G.resize( sz );
        vis.clear(); vis.resize( sz );
        dis.clear(); dis.resize( sz );
        cur.clear(); cur.resize( sz );
    }
    void add_edge( int from, int to, int cap ){
        edge.push_back( Nedge( from, to, cap, 0 ) );
        edge.push_back( Nedge( to, from, 0, 0 ) );
        m = edge.size();
        G[ from ].push_back( m - 2 );
        G[ to ].push_back( m - 1 );
    }
    bool BFS(){
        fill( vis.begin(), vis.end(), 0 );
        queue< int > que;
        que.push( s );
        dis[ s ] = 0;
        vis[ s ] = 1;
        while( not que.empty() ){
            int x = que.front(); que.pop();
            for( int i = 0; i < G[ x ].size(); ++i ){
                Nedge &e = edge[ G[ x ][ i ] ];
                if( not vis[ e.to ] and e.cap > e.flow )
                    vis[ e.to ] = 1,
                    dis[ e.to ] = dis[ x ] + 1,
                    que.push( e.to );
            }
        }
        return vis[ t ];
    }
    int DFS( int x, int a ){
        if( x == t or a == 0 ) return a;
        int flow = 0, f;
        for( int &i = cur[ x ]; i < G[ x ].size(); ++i ){
            Nedge &e = edge[ G[ x ][ i ] ];
            if( dis[ x ] + 1 == dis[ e.to ] and ( f = DFS( e.to, min( a, e.cap - e.flow ) ) ) > 0 ){
                e.flow += f;
                edge[ G[ x ][ i ] ^ 1 ].flow -= f;
                flow += f;
                a -= f;
                if( a == 0 ) break;
            }
        }
        return flow;
    }
    int max_flow( int s, int t ){
        this->s = s; this->t = t;
        int flow = 0;
        while( BFS() )
            fill( cur.begin(), cur.end(), 0 ),
            flow += DFS( s, INF );
        return flow;
    }
};

void solve(){
    int N, K; cin >> N >> K;
    vvi stock( N, vi( K ) );
    for( int i = 0; i < N; ++i )
        for( int j = 0; j < K; ++j )
            cin >> stock[ i ][ j ];
    Dinic *din = new Dinic( 2 * N + 2 );
    for( int i = 0; i < N; ++i )
        din->add_edge( 2 * N, i * 2, 1 ), // s = 2 * N
        din->add_edge( i * 2 + 1, 2 * N + 1, 1 ); // t = 2 * N + 1
    for( int i = 0; i < N; ++i )
        for( int j = 0; j < N; ++j ) if( i != j ){
            bool ng = false;
            for( int k = 0; k < K; ++k )
                if( not ( stock[ i ][ k ] < stock[ j ][ k ] ) )
                    ng = true;
            if( not ng ) din->add_edge( i * 2, j * 2 + 1, 1 );
        }
    cout << N - din->max_flow( 2 * N, 2 * N + 1 ) << "\n";
}