# Description
观察题意,一个点被走过之后权值变为 0,但是还可以继续走,所以我们可以把一个点拆成入点和出点,然后从入点向出点连两条边。
# Solution
- 网络流
题意即为选取一个点之后,当前点上下左右均不能选择,求选取的点的最大权值和。
所以我们把矩阵黑白染色,每个黑点向周围的 4 个白点连边,流量为正无穷。
然后建超级源点,超级汇点。源点向黑点连边,流量为黑点的权值;白点向汇点连边,流量为白点的权值。
然后我们对于这张图跑一个最小割,图中剩下的边权和就是能选取的最大和。
所以答案就是 减去最小割。最小割 = 最大流 相信大家都知道。
# Code
#include <bits/stdc++.h> | |
#define P(i, j) ((i - 1) * m + j) | |
using namespace std; | |
namespace IO{ | |
inline int read(){ | |
int x = 0; | |
char ch = getchar(); | |
while(!isdigit(ch)) ch = getchar(); | |
while(isdigit(ch)) x = (x << 3) + (x << 1) + ch - '0', ch = getchar(); | |
return x; | |
} | |
template <typename T> inline void write(T x){ | |
if(x > 9) write(x / 10); | |
putchar(x % 10 + '0'); | |
} | |
} | |
using namespace IO; | |
const int N = 2010; | |
const int INF = 2e9; | |
int n, m, sum, ans, s, t; | |
struct node{ | |
int v, w, nxt; | |
}edge[N << 1]; | |
int head[N], tot = 1; | |
int dx[4] = {-1, 0, 1, 0}; | |
int dy[4] = {0, 1, 0, -1}; | |
inline void add(int x, int y, int z){ | |
edge[++tot] = (node){y, z, head[x]}; | |
head[x] = tot; | |
} | |
int dep[N]; | |
inline bool bfs(){ | |
queue <int> q; | |
memset(dep, 0, sizeof(dep)); | |
q.push(s), dep[s] = 1; | |
while(!q.empty()){ | |
int x = q.front(); | |
q.pop(); | |
for(int i = head[x]; i; i = edge[i].nxt){ | |
int y = edge[i].v; | |
if(edge[i].w && !dep[y]) | |
dep[y] = dep[x] + 1, q.push(y); | |
} | |
} | |
return dep[t]; | |
} | |
inline int dfs(int x, int lim){ | |
if(x == t || !lim) return lim; | |
int flow = 0; | |
for(int i = head[x]; i; i = edge[i].nxt){ | |
int y = edge[i].v; | |
if(edge[i].w && dep[y] == dep[x] + 1){ | |
int res = dfs(y, min(lim, edge[i].w)); | |
lim -= res, flow += res, edge[i].w -= res, edge[i ^ 1].w += res; | |
} | |
} | |
if(!flow) dep[x] = 0; | |
return flow; | |
} | |
inline void dinic(){ | |
while(bfs()) ans += dfs(s, INF); | |
} | |
inline bool check(int x, int y){ | |
return x >= 1 && x <= n && y >= 1 && y <= m; | |
} | |
int main(){ | |
n = read(), m = read(); | |
t = P(n, m) + 1; | |
for(int i = 1, z; i <= n; ++i) | |
for(int j = 1; j <= m; ++j){ | |
sum += (z = read()); | |
if(!((i + j) & 1)){ | |
add(s, P(i, j), z), add(P(i, j), s, 0); | |
for(int k = 0; k < 4; ++k){ | |
int x = i + dx[k], y = j + dy[k]; | |
if(check(x, y)) add(P(i, j), P(x, y), INF), add(P(x, y), P(i, j), 0); | |
} | |
}else add(P(i, j), t, z), add(t, P(i, j), 0); | |
} | |
dinic(); | |
write(sum - ans), puts(""); | |
return 0; | |
} |