题目链接:https://www.luogu.org/problemnew/show/P2774
题目描述
在一个有 m*n 个方格的棋盘中,每个方格中有一个正整数。现要从方格中取数,使任意 2 个数所在方格没有公共边,且取出的数的总和最大。试设计一个满足要求的取数算法。对于给定的方格棋盘,按照取数要求编程找出总和最大的数。
输入输出格式
输入格式:
第 1 行有 2 个正整数 m 和 n,分别表示棋盘的行数和列数。接下来的 m 行,每行有 n 个正整数,表示棋盘方格中的数。
输出格式:
程序运行结束时,将取数的最大总和输出
输入输出样例
输入样例#1: 复制
3 3 1 2 3 3 2 3 2 3 1输出样例#1: 复制
11说明
m,n<=100
将每个点和它相邻的点标记为不同的颜色,然后把白点和S相连,黑点和T相连,容量为这个点的权值。
然后把每个白点和它相邻的黑点相连,容量为inf。
然后问题就转化为求二分图最大点权独立集。
根据某个定理,答案就等于总权值减去最小割。#pragma GCC optimize(2) #include<stdio.h> #include<algorithm> #include<string.h> #include<queue> using namespace std; const int maxn = 500; const int inf = 0x3f3f3f3f; typedef long long ll; using namespace std; struct data { int nex, to, w; }e[12000]; int head[1000], edge = -1; int f[32][32], a[32][32], id[32][32], lev[1000]; void add(int from, int to, int w) { e[++edge].nex = head[from]; e[edge].to = to; e[edge].w = w; head[from] = edge; } inline bool bfs(int s, int t) { queue<int>q; q.push(s); memset(lev, 0, sizeof(lev)); lev[s] = 1; while (!q.empty()) { int u = q.front(); q.pop(); for (int i = head[u]; i != -1; i = e[i].nex) if (e[i].w>0 && !lev[e[i].to]) { lev[e[i].to] = lev[u] + 1; q.push(e[i].to); if (e[i].to == t) return 1; } } return 0; } int dfs(int s, int t, int k) { if (s == t) return k; int tag = 0; for (int i = head[s]; i != -1; i = e[i].nex) if (e[i].w>0 && lev[e[i].to] == lev[s] + 1) { int d = dfs(e[i].to, t, min(k - tag, e[i].w)); e[i].w -= d; e[i ^ 1].w += d; tag += d; if (tag == k) return tag; } return tag; } int dinic(int s, int t) { int flow = 0; while (bfs(s, t)) flow += dfs(s, t, inf); return flow; } int main() { //freopen("C://input.txt", "r", stdin); memset(head, -1, sizeof(head)); int n, m; scanf("%d%d", &n, &m); int s = 0, t = n * m + 1, tot = 0; for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) scanf("%d", &a[i][j]), tot += a[i][j]; f[1][1] = 1; for (int i = 2; i <= m; i++) f[1][i] = f[1][i - 1] ^ 1; for (int i = 2; i <= n; i++) for (int j = 1; j <= m; j++) f[i][j] = f[i - 1][j] ^ 1; for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) id[i][j] = (i - 1)*m + j; for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) { int k = id[i][j]; if (f[i][j]) { add(s, k, a[i][j]), add(k, s, 0); if (i - 1>0) add(k, id[i - 1][j], inf), add(id[i - 1][j], k, 0); if (i + 1 <= n) add(k, id[i + 1][j], inf), add(id[i + 1][j], k, 0); if (j - 1>0) add(k, id[i][j - 1], inf), add(id[i][j - 1], k, 0); if (j + 1 <= m) add(k, id[i][j + 1], inf), add(id[i][j + 1], k, 0); } else add(k, t, a[i][j]), add(t, k, 0); } printf("%d\n", tot - dinic(s, t)); return 0; }
网络流24题 P2774 方格取数问题 (最小割)
最新推荐文章于 2020-08-18 11:42:52 发布