大家都很強, 可與之共勉 。
題意:
在一個有
現要從方格中取數,使任意
題解:
求一張圖的最大點權獨立集。
關於概念:
二分圖最大點權獨立集:從一張圖中找到權值和最大的點集,使得它們之間兩兩沒有邊。
二分圖最小點權覆蓋集:從一張圖中選取一些點,使這些點覆蓋所有的邊,並且選出來的點的權值儘可能小。
其實二分圖最大點權獨立集是二分圖最小點權覆蓋的對偶問題。即是總權值-二分圖最小點覆蓋集。
而二分圖最小點權覆蓋集等於二分圖最小割
我們把原圖黑白染色以
我們只連有衝突的邊,邊權爲
跑出最小割,即二分圖最小點權覆蓋集。然後用總權值減去就是答案。
# 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 ;
}