题意
传送门 P4313 文理分科
题解
记图的 s − t s-t s−t 割中,包含源点 s s s 的集合为 S S S, 包含汇点 t t t 的集合为 T T T。假设被 S S S 包含的节点选择文科,反之选择理科。将问题从求解最大值转换为求解最小值
∑ i , j ( a r t i , j + s c i e n c e i , j + s a m e _ a r t i , j + s a m e _ s c i e n c e i , j ) − ( ∑ ( i , j ) ∈ T a r t i , j + ∑ ( i , j ) ∈ S s c i e n c e i , j + ∑ ( i , j ) 与 相 邻 格 子 至 少 存 在 一 个 属 于 T s a m e _ a r t i , j + ∑ ( i , j ) 与 相 邻 格 子 至 少 存 在 一 个 属 于 S s a m e _ s c i e n c e i , j ) \sum\limits_{i,j} (art_{i,j} + science_{i,j} + same\_art_{i,j} + same\_science_{i,j}) \\- (\sum\limits_{(i,j)\in T}art_{i,j}+ \sum\limits_{(i,j)\in S}science_{i,j}+ \sum\limits_{(i,j)与相邻格子至少存在一个属于T}same\_art_{i,j}+\sum\limits_{(i,j)与相邻格子至少存在一个属于S}same\_science_{i,j}) i,j∑(arti,j+sciencei,j+same_arti,j+same_sciencei,j)−((i,j)∈T∑arti,j+(i,j)∈S∑sciencei,j+(i,j)与相邻格子至少存在一个属于T∑same_arti,j+(i,j)与相邻格子至少存在一个属于S∑same_sciencei,j) 最小化后一项即可。
问题转化为如何建边,使花费的总和等于割的总容量。令
(
u
,
v
,
c
)
(u,v,c)
(u,v,c) 代表
u
→
v
u\rightarrow v
u→v 连一条容量为
c
c
c 的边。令
(
i
,
j
)
(i,j)
(i,j) 代表的节点编号为
i
×
m
+
j
i\times m +j
i×m+j。对于
∑
(
i
,
j
)
∈
T
a
r
t
i
,
j
\sum\limits_{(i,j)\in T}art_{i,j}
(i,j)∈T∑arti,j 建边
(
s
,
i
×
m
+
j
,
a
r
t
i
,
j
)
(s,i\times m +j,art_{i,j})
(s,i×m+j,arti,j)。对于
∑
(
i
,
j
)
∈
S
s
c
i
e
n
c
e
i
,
j
\sum\limits_{(i,j)\in S}science_{i,j}
(i,j)∈S∑sciencei,j 建边
(
i
×
m
+
j
,
t
,
s
c
i
e
n
c
e
i
,
j
)
(i\times m +j,t,science_{i,j})
(i×m+j,t,sciencei,j)。对于
∑
(
i
,
j
)
与
相
邻
格
子
至
少
存
在
一
个
属
于
T
s
a
m
e
_
a
r
t
i
,
j
\sum\limits_{(i,j)与相邻格子至少存在一个属于T}same\_art_{i,j}
(i,j)与相邻格子至少存在一个属于T∑same_arti,j 设
a
d
j
adj
adj 代表
(
i
,
j
)
(i,j)
(i,j) 与相邻格子构成的点集,新建虚节点
v
v
v,建边
(
s
,
v
,
s
a
m
e
_
a
r
t
i
,
j
)
(s,v,same\_art_{i,j})
(s,v,same_arti,j) 与
(
v
,
u
,
∞
)
,
u
∈
a
d
j
(v,u,\infty),u\in adj
(v,u,∞),u∈adj。对于最后一项同理。
#include <bits/stdc++.h>
using namespace std;
#define pb push_back
const int MAXN = 105, MAXV = 3E4 + 5, INF = 0x3f3f3f3f;
int N, M, V;
int A[MAXN][MAXN], B[MAXN][MAXN];
int _A[MAXN][MAXN], _B[MAXN][MAXN];
struct edge
{
int to, cap, rev;
};
vector<edge> G[MAXV];
int iter[MAXV], level[MAXV];
void add_edge(int from, int to, int cap)
{
G[from].pb({to, cap, G[to].size()});
G[to].pb({from, 0, G[from].size() - 1});
}
void bfs(int s)
{
for (int v = 0; v < V; ++v)
level[v] = -1;
queue<int> q;
level[s] = 0;
q.push(s);
while (q.size())
{
int v = q.front();
q.pop();
for (auto &e : G[v])
if (e.cap > 0 && level[e.to] == -1)
level[e.to] = level[v] + 1, q.push(e.to);
}
}
int dfs(int v, int t, int f)
{
if (v == t)
return f;
for (int &i = iter[v]; i < G[v].size(); ++i)
{
auto &e = G[v][i];
if (e.cap > 0 && level[e.to] > level[v])
{
int d = dfs(e.to, t, min(f, e.cap));
if (d > 0)
{
e.cap -= d;
G[e.to][e.rev].cap += d;
return d;
}
}
}
return 0;
}
int max_flow(int s, int t)
{
int flow = 0;
for (;;)
{
bfs(s);
if (level[t] == -1)
return flow;
for (int v = 0; v < V; ++v)
iter[v] = 0;
int f;
while ((f = dfs(s, t, INF)) > 0)
flow += f;
}
}
const int dx[5] = {0, 0, -1, 1, 0}, dy[5] = {-1, 1, 0, 0, 0};
int main()
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> N >> M;
for (int i = 0; i < N; ++i)
for (int j = 0; j < M; ++j)
cin >> A[i][j];
for (int i = 0; i < N; ++i)
for (int j = 0; j < M; ++j)
cin >> B[i][j];
for (int i = 0; i < N; ++i)
for (int j = 0; j < M; ++j)
cin >> _A[i][j];
for (int i = 0; i < N; ++i)
for (int j = 0; j < M; ++j)
cin >> _B[i][j];
int res = 0;
for (int i = 0; i < N; ++i)
for (int j = 0; j < M; ++j)
res += A[i][j] + B[i][j] + _A[i][j] + _B[i][j];
int s = N * M, t = s + 1;
V = t + 1;
for (int i = 0; i < N; ++i)
for (int j = 0; j < M; ++j)
add_edge(s, i * M + j, A[i][j]), add_edge(i * M + j, t, B[i][j]);
for (int x = 0; x < N; ++x)
for (int y = 0; y < M; ++y)
{
int v = V++, u = V++;
add_edge(s, v, _A[x][y]);
add_edge(u, t, _B[x][y]);
for (int d = 0; d < 5; ++d)
{
int nx = x + dx[d], ny = y + dy[d];
if (0 <= nx && nx < N && 0 <= ny && ny < M)
{
int w = nx * M + ny;
add_edge(v, w, INF);
add_edge(w, u, INF);
}
}
}
res -= max_flow(s, t);
cout << res << '\n';
return 0;
}