题面
题解
发现一种地要么归 (A) , 要么归 (B) , 若相邻两块归属不同还有额外代价
那么 (A) 买就连源点, (B) 买就连汇点
总收益加上这些值
中间连代价的双向边
答案为总收益减去最小割
Code
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
#include <cmath>
const int N = 160005;
const int INF = 0x3f3f3f3f;
using namespace std;
int n, m, id[405][405], head[N], S, T, cnte = 1, d[N], cur[N], ans;
struct edge { int to, nxt, flow; } e[N << 4];
queue<int> q;
template < typename T >
inline T read()
{
T x = 0, w = 1; char c = getchar();
while(c < '0' || c > '9') { if(c == '-') w = -1; c = getchar(); }
while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return x * w;
}
inline void adde(int u, int v, int w)
{
e[++cnte] = (edge) { v, head[u], w }, head[u] = cnte;
e[++cnte] = (edge) { u, head[v], 0 }, head[v] = cnte;
}
bool bfs()
{
memset(d, 0, sizeof(d)), d[S] = 1, q.push(S);
while(!q.empty())
{
int u = q.front(); q.pop();
for(int v, i = head[u]; i; i = e[i].nxt)
{
v = e[i].to;
if(e[i].flow > 0 && !d[v])
d[v] = d[u] + 1, q.push(v);
}
}
return d[T];
}
int dfs(int u, int a)
{
if(u == T || !a) return a;
int f = 0;
for(int v, i = head[u]; i; i = e[i].nxt)
{
v = e[i].to;
if(e[i].flow > 0 && d[v] == d[u] + 1)
{
int tmp = dfs(v, min(a, e[i].flow));
a -= tmp, f += tmp, e[i].flow -= tmp, e[i ^ 1].flow += tmp;
}
if(!a) break;
}
if(a) d[u] = 0;
return f;
}
int dinic()
{
int flow = 0;
while(bfs())
flow += dfs(S, INF);
return flow;
}
int main()
{
n = read <int> (), m = read <int> (), S = n * m + 1, T = S + 1;
for(int x, i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
{
id[i][j] = (i - 1) * m + j, x = read <int> ();
if(x >= 0) adde(S, id[i][j], x);
else adde(id[i][j], T, -x);
ans += (x > 0 ? x : -x);
}
for(int x, i = 1; i < n; i++)
for(int j = 1; j <= m; j++)
{
x = read <int> ();
adde(id[i][j], id[i + 1][j], x);
adde(id[i + 1][j], id[i][j], x);
}
for(int x, i = 1; i <= n; i++)
for(int j = 1; j < m; j++)
{
x = read <int> ();
adde(id[i][j], id[i][j + 1], x);
adde(id[i][j + 1], id[i][j], x);
}
ans -= dinic();
printf("%d
", ans);
return 0;
}