ABC225G X
看到题,dp?no。
如果想要 dp,至少要知道三个方向上的情况,不管什么顺序都很难 dp。
看到如同我的智商一样低的数据范围,很像 wll。
选一些物品使得代价最大,还有额外代价,考虑最小割,先把每个点向 \(\texttt{T}\) 连价值为 \(a_{i, j}\) 的边如果这些边被割即代表不要这个格子了。
那么 \(\texttt{S}\) 那边怎么连边,我们实际上是在求各个方向上的连通块个数,这个限制太全局了,把它放到每个点上,单独考虑一个方向上的情况。
如果一个点的前驱不选,而它选了,则带来 \(C\) 的贡献,把它视作连通块的起点。而如果前面有一个起点,中间的点都划了另一个方向上的边,它就可以免费划线。
具体来说,对应到网络流上就是:左上和右上向该点连边,如果没有点就从 \(\texttt{S}\) 连边,代价都为 \(C\)。割掉连向该点的一条边对应把它作为某个方向上的起点。能否免费划线的限制恰好与最小割的限制相同。
// No mind to think.
//
// No will to break.
//
// No voice to cry suffering.
//
// Born of God and Void.
//
// You shall seal the blinding light that plagues their dreams.
//
// You are the Vessel.
//
// You are the Hollow Knight.
#ifdef N2
#define _GLIBCXX_DEBUG
#define LOG(...) fprintf(stderr, __VA_ARGS__)
#define DO(...) __VA_ARGS__
#else
#define LOG(...) void(0)
#define DO(...) void(0)
#define NDEBUG
#endif
#define syncoff ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10;
using ll = long long;
int dis[maxn];
int S = maxn - 2, T = maxn - 1;
struct Edge {
int v, rev;
ll flow;
Edge() = default;
Edge(int v, ll flow, int rev) : v(v), flow(flow), rev(rev) {}
};
vector<Edge> G[maxn];
int cur[maxn];
void Add(int u, int v, ll w) {
// cerr << u << ' ' << v << ' ' << w << '\n';
G[u].emplace_back(v, w, G[v].size());
G[v].emplace_back(u, 0, G[u].size() - 1);
}
bool Bfs() {
memset(dis, 0, sizeof(dis));
queue<int> q;
dis[S] = 1;
q.emplace(S);
while(!q.empty()) {
int u = q.front(); q.pop();
for(auto e : G[u]) {
if(dis[e.v] || !e.flow) continue;
dis[e.v] = dis[u] + 1;
q.emplace(e.v);
}
}
return dis[T];
}
ll Dfs(int u, ll in) {
if(u == T) return in;
ll out = 0;
for(int &i = cur[u]; i < G[u].size(); i++) {
auto &e = G[u][i];
if(dis[e.v] != dis[u] + 1 || !e.flow) continue;
int del = Dfs(e.v, min(e.flow, in - out));
e.flow -= del, G[e.v][e.rev].flow += del;
out += del;
if(in == out) break;
}
return out;
}
ll Dinic() {
ll res = 0;
while(Bfs()) {
memset(cur, 0, sizeof(cur));
res += Dfs(S, 0x3f3f3f3f3f3f3f3f);
}
return res;
}
const int maxl = 110;
int a[maxl][maxl];
ll sum;
int n, m, c;
bool In(int x, int y) {
return x > 0 && x <= n && y > 0 && y <= m;
}
int Map(int x, int y) {
return (x - 1) * m + y;
}
int main() {
cin >> n >> m >> c;
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++) {
cin >> a[i][j];
sum += a[i][j];
Add(Map(i, j), T, a[i][j]);
if(In(i - 1, j - 1)) Add(Map(i - 1, j - 1), Map(i, j), c);
else Add(S, Map(i, j), c);
if(In(i - 1, j + 1)) Add(Map(i - 1, j + 1), Map(i, j), c);
else Add(S, Map(i, j), c);
}
}
cout << sum - Dinic();
}