# Description

观察题意,一个点被走过之后权值变为 0,但是还可以继续走,所以我们可以把一个点拆成入点和出点,然后从入点向出点连两条边。

# Solution

  • 网络流

题意即为选取一个点之后,当前点上下左右均不能选择,求选取的点的最大权值和。

所以我们把矩阵黑白染色,每个黑点向周围的 4 个白点连边,流量为正无穷。

然后建超级源点,超级汇点。源点向黑点连边,流量为黑点的权值;白点向汇点连边,流量为白点的权值。

然后我们对于这张图跑一个最小割,图中剩下的边权和就是能选取的最大和。

所以答案就是 sumsum 减去最小割。最小割 = 最大流 相信大家都知道。

# 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;
}
更新于 阅读次数