您的位置:首页 > 理论基础 > 计算机网络

LOJ6007 「网络流 24 题 - 8」 方格取数 二分图最大点权独立集

2017-12-07 12:25 459 查看

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

题意:

  在一个有 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
, cur
, ecnt ;
int S, T , dep
;

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 ;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: