把切点看作割边。新建一个虚拟的层
R+1
,建立源点
S,T
。
先不考虑光滑限制。先由源点
S
向第
然后对于任何一个
1≤i≤P,1≤j≤Q,1≤k≤R
,由
(i,j,k)
向
(i,j,k+1)
连一条容量为
(i,j,k)
的不和谐值的边。
显然,对于任何一个
1≤i≤P,1≤j≤Q
,从
(i,j,1)
到
(i,j,R+1)
的路径上需要且只需要割掉一条边。
再考虑光滑限制。可以发现对于任意一个在同一平面上距离为
1
的两个点对
怎样限制这个条件呢?可以发现,我们的目标其实就是让
f(i,j)−f(x,y)>D
时,
S
仍然可以到达
最后求最小割(最大流)即为答案。
代码:
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
inline int read() {
int res = 0; bool bo = 0; char c;
while (((c = getchar()) < '0' || c > '9') && c != '-');
if (c == '-') bo = 1; else res = c - 48;
while ((c = getchar()) >= '0' && c <= '9')
res = (res << 3) + (res << 1) + (c - 48);
return bo ? ~res + 1 : res;
}
const int W = 45, N = 3e5 + 5, INF = 0x3f3f3f3f;
int P, Q, R, D, S, T, val[W][W][W], ecnt = 1, nxt[N], adj[N], go[N], cap[N],
lev[N], len, que[N], dx[] = {-1, 1, 0, 0}, dy[] = {0, 0, -1, 1};
void add_edge(int u, int v, int w) {
nxt[++ecnt] = adj[u]; adj[u] = ecnt; go[ecnt] = v; cap[ecnt] = w;
nxt[++ecnt] = adj[v]; adj[v] = ecnt; go[ecnt] = u; cap[ecnt] = 0;
}
bool bfs() {
int i; memset(lev, -1, sizeof(lev));
lev[que[len = 1] = S] = 0;
for (i = 1; i <= len; i++) {
int u = que[i];
for (int e = adj[u], v; e; e = nxt[e])
if (cap[e] > 0 && lev[v = go[e]] == -1) {
lev[que[++len] = v] = lev[u] + 1;
if (v == T) return 1;
}
}
return 0;
}
int dinic(int u, int flow) {
if (u == T) return flow;
int res = 0, delta;
for (int e = adj[u], v; e; e = nxt[e])
if (cap[e] > 0 && lev[u] < lev[v = go[e]]) {
delta = dinic(v, min(cap[e], flow - res));
if (delta) {
cap[e] -= delta; cap[e ^ 1] += delta;
res += delta; if (res == flow) break;
}
}
if (res != flow) lev[u] = -1;
return res;
}
int solve() {
int ans = 0;
while (bfs()) ans += dinic(S, INF);
return ans;
}
int main() {
int i, j, k, h; P = read(); Q = read();
R = read(); D = read(); S = 1; T = P * Q * (R + 1) + 2;
for (k = 1; k <= R; k++) for (i = 1; i <= P; i++)
for (j = 1; j <= Q; j++) val[i][j][k] = read();
for (i = 1; i <= P; i++) for (j = 1; j <= Q; j++) {
int x = (i - 1) * Q + j + 1;
add_edge(S, x, INF); for (k = 1; k <= R; k++)
add_edge(P * Q * (k - 1) + x, P * Q * k + x, val[i][j][k]);
add_edge(P * Q * R + x, T, INF);
}
for (i = 1; i <= P; i++) for (j = 1; j <= Q; j++)
for (h = 0; h < 4; h++) {
int x = i + dx[h], y = j + dy[h];
if (x < 1 || x > P || y < 1 || y > Q) continue;
for (k = D + 1; k <= R + 1; k++)
add_edge(P * Q * (k - 1) + (i - 1) * Q + j + 1,
P * Q * (k - D - 1) + (x - 1) * Q + y + 1, INF);
}
printf("%d\n", solve());
return 0;
}