luogu P2774 方格取数问题(最小割)

    科技2026-02-02  5

    题目描述

    有一个 mm 行 nn 列的方格图,每个方格中都有一个正整数。现要从方格中取数,使任意两个数所在方格没有公共边,且取出的数的总和最大,请求出最大的和。

    输入格式

    第一行是两个用空格隔开的整数,分别代表方格图的行数 mm 和列数 nn。

    第 22 到第 (m + 1)(m+1) 行,每行 nn 个整数,第 (i + 1)(i+1) 行的第 jj 个整数代表方格图第 ii 行第 jj 列的的方格中的数字 a_{i, j}ai,j​。

    输出格式

    输出一行一个整数,代表和最大是多少。

    输入输出样例

    输入 #1

    3 3 1 2 3 3 2 3 2 3 1

    输出 #1

    11

    说明/提示

    数据规模与约定

    对于 100\%100% 的数据,保证 1 \leq n, m \leq 1001≤n,m≤100,1 \leq a_{i, j} \leq 10^51≤ai,j​≤105。

    提示

    请注意输入的第一行先读入 mm 再读入 nn。

    思路:

    对棋盘进行黑白染色,横坐标 + 纵坐标为奇数的点染成黑色,为偶数的点染成白色,当取一个黑色的点时,和它相邻的白色点会受到影响,如下连边:

    (1)超级源点向所有的黑色点连边,容量为点权

    (2)所有的白色点向超级汇点连边,容量为点权

    (3)黑色点向与它相邻的四个白色点连边,容量为inf

    因为最小割 == 最大流,所以最大和 = 全局和 - 舍弃和

    so跑个最大流就OK

    #include<bits/stdc++.h> using namespace std; typedef long long ll; typedef unsigned long long ull; const int N = 1005; const int M = 10005; //边集二倍 const int mod = 1e9 + 7; const int inf = 0x3f3f3f3f; int head[N], dis[N], tot, n, m, s, t; struct Edge { int to, next, cap, flow; }edge[M]; void init() { tot = 2; memset(head, -1, sizeof(head)); } void addedge(int u, int v, int w, int rw = 0) { edge[tot].to = v; edge[tot].cap = w; edge[tot].flow = 0; edge[tot].next = head[u]; head[u] = tot++; edge[tot].to = u; edge[tot].cap = rw; edge[tot].flow = 0; edge[tot].next = head[v]; head[v] = tot++; } int Q[N]; int dep[N], cur[N], sta[N]; ///数组cur记录点u之前循环到了哪一条边 bool bfs(int s, int t, int n) { int fron = 0, tail = 0; memset(dep, -1, sizeof(dep[0]) * (n + 1)); dep[s] = 0; Q[tail++] = s; while(fron < tail) { int u = Q[fron++]; for(int i = head[u]; i != -1; i = edge[i].next) { int v = edge[i].to; if(edge[i].cap > edge[i].flow && dep[v] == -1) { dep[v] = dep[u] + 1; if(v == t) return true; Q[tail++] = v; } } } return false; } int dinic(int s, int t, int n) { int maxflow = 0; while(bfs(s, t, n)) { for(int i = 0; i <= n; ++i) cur[i] = head[i]; int u = s, tail = 0; while(cur[s] != -1) { if(u == t) { int tp = inf; for(int i = tail - 1; i >= 0; --i) tp = min(tp, edge[sta[i]].cap - edge[sta[i]].flow); maxflow += tp; for(int i = tail - 1; i >= 0; --i) { edge[sta[i]].flow += tp; edge[sta[i] ^ 1].flow -= tp; if(edge[sta[i]].cap - edge[sta[i]].flow == 0) tail = i; } u = edge[sta[tail] ^ 1].to; } else if(cur[u] != -1 && edge[cur[u]].cap > edge[cur[u]].flow && dep[u] + 1 == dep[edge[cur[u]].to]) { sta[tail++] = cur[u]; u = edge[cur[u]].to; } else { while(u != s && cur[u] == -1) u = edge[sta[--tail] ^ 1].to; cur[u] = edge[cur[u]].next; } } } return maxflow; } int main() { init(); scanf("%d%d", &n, &m); int sum = 0, a; s = 0, t = n * m + 1; for(int i = 1; i <= n; ++i) { for(int j = 1; j <= m; ++j) { scanf("%d", &a); sum += a; if((i + j) & 1) { addedge(s, (i - 1) * m + j, a); if(i + 1 <= n) addedge((i - 1) * m + j, i * m + j, inf); if(i - 1 >= 1) addedge((i - 1) * m + j, (i - 2) * m + j, inf); if(j + 1 <= m) addedge((i - 1) * m + j, (i - 1) * m + j + 1, inf); if(j - 1 >= 1) addedge((i - 1) * m + j, (i - 1) * m + j - 1, inf); } else addedge((i - 1) * m + j, t, a); } } int maxflow = dinic(s, t, t + 1); printf("%d\n", sum - maxflow); return 0; }

     

    Processed: 0.013, SQL: 9