P1935 [国家集训队] 圈地计划

先看一道较为类似的题:文理分科

在那道题里,我们的最小割模型中有以下几种边:

  1. Sid(i,j),边权为 arti,j

    其中,id(i,j) 表示在第 i 行第 j 列的人。这条边保留表示在这个人选文科;割掉则是选理科。

  2. id(i,j)T,边权为 sciencei

    这条边保留表示当前这个人选理科;反之选文科。

  3. 对于一个人及其上下左右的组合 k,我们建立虚点 num1k,num2k

    1. snum1k,边权为 sameart;以及 num1kid(i,j),边权为 INF(满足{i,j}k)。

    2. num2kt,边权为 sameart;以及 id(i,j)num2k,边权为 INF(满足{i,j}k)。

回归这道题。我们不妨把那个 k×Ci,j 拆成几个 Ci,j 相加,之后就只要考虑一个位置与它某个相邻位置的独立贡献了。之后就是上述文理分可经典模型。

但是还有个不同点:这题要求两个位置不一样才有贡献——考虑对棋盘黑白染色,对于黑点,我们还是按照 (Sid(i,j),arti,j),(id(i,j)T,sciencei,j) 的方式连边;对于白点,我们反一下,(Sid(i,j),sciencei,j),(id(i,j)T,arti,j)。问题得到解决。

#include <bits/stdc++.h>
#define FL(i, a, b) for(int i = (a); i <= (b); i++)
#define FR(i, a, b) for(int i = (a); i >= (b); i--)
using namespace std;
typedef long long ll;
const int S = 110, N = 100010, M = 260010;
const int f[4][2] = {0, 1, 1, 0, 0, -1, -1, 0};
const ll INF = 1e18;
struct E{int v; ll w, nxt;} e[M << 1];
int n, m, s, t, tot, tote; ll ans;
int a[S][S], b[S][S], head[N], now[N], dep[N];
void init(){
    tote = 0;
    memset(head, -1, sizeof(head));
}
void Adde(int u, int v, ll w){
    e[tote] = {v, w, head[u]}, head[u] = tote++;
}
void Add(int u, int v, ll w){
    Adde(u, v, w), Adde(v, u, 0);
}
int id(int x, int y){
    return (x - 1) * m + y;
}
int bfs(){
    queue<int> q; q.push(s);
    memset(dep, 0, sizeof(dep)), dep[s] = 1;
    while(!q.empty()){
        int u = q.front(); q.pop();
        for(int i = now[u] = head[u]; ~i; i = e[i].nxt)
            if(e[i].w && !dep[e[i].v])
                q.push(e[i].v), dep[e[i].v] = dep[u] + 1;
    }
    return dep[t];
}
ll dfs(int u, ll in){
    if(u == t) return in; ll out = 0;
    for(int i = now[u]; ~i && in; i = e[i].nxt){
        int v = e[i].v; ll w = e[i].w; now[u] = i;
        if(dep[v] == dep[u] + 1 && w){
            ll flow = dfs(v, min(in, (ll)w));
            in -= flow, out += flow;
            e[i].w -= flow, e[i ^ 1].w += flow;
        }
    }
    if(!out) dep[u] = 0; return out;
}
ll dinic(){
    ll ret = 0;
    while(bfs()) ret += dfs(s, INF);
    return ret;
}
int main(){
    scanf("%d%d", &n, &m);
    init(), s = 0, t = ++(tot = n * m);
    FL(i, 1, n) FL(j, 1, m){
        scanf("%d", &a[i][j]), ans += a[i][j];
        if((i + j) & 1) Add(s, id(i, j), a[i][j]);
        else Add(id(i, j), t, a[i][j]);
    }
    FL(i, 1, n) FL(j, 1, m){
        scanf("%d", &b[i][j]), ans += b[i][j];
        if((i + j) & 1) Add(id(i, j), t, b[i][j]);
        else Add(s, id(i, j), b[i][j]);
    }
    FL(i, 1, n) FL(j, 1, m){
        int val; scanf("%d", &val);
        FL(k, 0, 3){
            int x = i + f[k][0], y = j + f[k][1];
            if(x < 1 || x > n || y < 1 || y > m) continue;
            ans += val, Add(s, ++tot, val);
            Add(tot, id(i, j), INF), Add(tot, id(x, y), INF);
            ans += val, Add(++tot, t, val);
            Add(id(i, j), tot, INF), Add(id(x, y), tot, INF);
        }
    }
    printf("%lld\n", ans - dinic());
    return 0;
}
posted @   徐子洋  阅读(7)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示