LOJ6007 「網絡流 24 題 - 8」 方格取數 二分圖最大點權獨立集

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

題意:

  在一個有 m×n 個方格的棋盤中,每個方格中有一個正整數。

  現要從方格中取數,使任意2 個數所在方格沒有公共邊,且取出的數的總和最大。試設計一個滿足要求的取數算法。

題解:

  求一張圖的最大點權獨立集。

  關於概念:

  二分圖最大點權獨立集:從一張圖中找到權值和最大的點集,使得它們之間兩兩沒有邊。
  二分圖最小點權覆蓋集:從一張圖中選取一些點,使這些點覆蓋所有的邊,並且選出來的點的權值儘可能小。
  其實二分圖最大點權獨立集是二分圖最小點權覆蓋的對偶問題。即是總權值-二分圖最小點覆蓋集。

  而二分圖最小點權覆蓋集等於二分圖最小割

  我們把原圖黑白染色以(i+j)%2 ,其中讓白色點在左邊,黑色點在右邊,白色點向黑色點連邊
  我們只連有衝突的邊,邊權爲+ ,對於每一個白點,S 向白點連邊,權值爲點權v ;對於每一個黑點,黑點向T 連邊,權值爲點權v

  跑出最小割,即二分圖最小點權覆蓋集。然後用總權值減去就是答案。

# 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] ;

    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 ;
                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 ;
        int rt = 0 ;
        while ( bfs ( S, T ) )    {
            memcpy ( cur, head, sizeof ( int ) * ( T + 1 ) ) ; 
            rt += dfs ( S, 0x3f3f3f3f ) ;
        }
        return rt ;
    }
} Lazer ;

# define id(i, j)  ((i-1)*m+j)

const int dx [] = { 1, -1, 0, 0 } ;
const int dy [] = { 0, 0, 1, -1 } ;

int main ( )  {
    int n, m ;
    scanf ( "%d%d", & n, & m ) ;
    const int S = n * m + 1, T = n * m + 2 ;

    int sum ( 0 ) ;

    for ( int i = 1 ; i <= n ; ++ i )
        for ( int j = 1 ; j <= m ; ++ j )  {
            int v ;
            scanf ( "%d", & v ) ;
            sum += v ;
            if ( ( i + j ) & 1 )  Lazer.add_edge ( S, id ( i, j ), v ) ;
            else Lazer.add_edge ( id ( i, j ), T, v ) ;
            if ( ( i + j ) & 1 )  {
                for ( int k = 0 ; k < 4 ; ++ k )  {
                    int x = i + dx [k], y = j + dy [k] ;
                    if ( x >= 1 && x <= n && y >= 1 && y <= m )  {
                        Lazer.add_edge ( id ( i, j ), id ( x, y ), 0x3f3f3f3f ) ;
                    }
                }
            }
        }

    int mincut = Lazer.dinic ( S, T ) ;

    printf ( "%d\n", sum - mincut ) ;

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