版权声明:欢迎转载蒟蒻博客,但请注明出处:blog.csdn.net/lpa20020220 https://blog.csdn.net/LPA20020220/article/details/82349155
洛谷传送门
BZOJ传送门
题目描述
在一个有 个方格的棋盘中,每个方格中有一个正整数。现要从方格中取数,使任意 个数所在方格没有公共边,且取出的数的总和最大。试设计一个满足要求的取数算法。对于给定的方格棋盘,按照取数要求编程找出总和最大的数。
输入输出格式
输入格式:
第 行有 个正整数 和 ,分别表示棋盘的行数和列数。接下来的 行,每行有 个正整数,表示棋盘方格中的数。
输出格式:
程序运行结束时,将取数的最大总和输出
输入输出样例
输入样例#1:
3 3
1 2 3
3 2 3
2 3 1
输出样例#1:
11
说明
解题分析
网络流经典模型: 棋盘式。
考虑到一个格子只与其周围的四个格子冲突, 那么我们可以把所有格子染成黑白两色棋盘式, 从源点向黑点连流量为格子收益的边,白点向汇点连格子收益的边, 然后冲突的黑点向白点连边。 这样求出的最小割就是需要舍弃掉的最小值, 我们用总收益去减就好了。
代码如下:
#include <cstdio>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <cstdlib>
#include <cmath>
#include <queue>
#define R register
#define IN inline
#define W while
#define MX 10050
#define gc getchar()
#define S 0
#define T 10001
#define INF 100000000
#define ll long long
template <class TT>
IN void in(TT &x)
{
x = 0; R char c = gc;
for (; !isdigit(c); c = gc);
for (; isdigit(c); c = gc)
x = (x << 1) + (x << 3) + c - 48;
}
int m, n, cnt;
int head[MX], layer[MX];
struct Edge {int to, fl, nex;} edge[MX << 4];
IN int getid(R int x, R int y) {return (x - 1) * n + y;}
IN void add(R int from, R int to, R int fl)
{
edge[++cnt] = {to, fl, head[from]}, head[from] = cnt;
edge[++cnt] = {from, 0, head[to]}, head[to] = cnt;
}
int mp[105][105];
std::queue <int> q;
IN bool BFS()
{
std::memset(layer, 0, sizeof(layer));
layer[S] = 1; q.push(S); R int now, i;
W (!q.empty())
{
now = q.front(); q.pop();
for (i = head[now]; ~i; i = edge[i].nex)
{
if(edge[i].fl > 0 && !layer[edge[i].to])
layer[edge[i].to] = layer[now] + 1, q.push(edge[i].to);
}
}
return layer[T];
}
int DFS(R int now, R int val)
{
if(now == T) return val;
R int lef = val, buf;
for (R int i = head[now]; ~i; i = edge[i].nex)
{
if(edge[i].fl > 0 && layer[edge[i].to] == layer[now] + 1)
{
buf = DFS(edge[i].to, std::min(lef, edge[i].fl));
if(!buf) continue;
edge[i].fl -= buf, lef -= buf, edge[i ^ 1].fl += buf;
if(lef <= 0) return val;
}
}
return val - lef;
}
IN ll init()
{
ll ret = 0;
W (BFS()) ret += DFS(S, INF);
return ret;
}
int main(void)
{
int s, id; in(m); n = m; ll sum = 0;
std::memset(head, cnt = -1, sizeof(head));
for (R int i = 1; i <= m; ++i)
for (R int j = 1; j <= n; ++j)
in(mp[i][j]), sum += mp[i][j];
for (R int i = 1; i <= m; ++i)
{
s = (i & 1) ? 1 : 2;
for (; s <= n; s += 2)
{
id = getid(i, s);
add(S, id, mp[i][s]);
if(s > 1) add(id, getid(i, s - 1), INF);
if(s < n) add(id, getid(i, s + 1), INF);
if(i > 1) add(id, getid(i - 1, s), INF);
if(i < m) add(id, getid(i + 1, s), INF);
}
}
for (R int i = 1; i <= m; ++i)
{
s = (i & 1) ? 2 : 1;
for (; s <= n; s += 2) add(getid(i, s), T, mp[i][s]);
}
printf("%lld", sum - init());
}