m * n 的矩阵中取数,不能取相邻的数,求能取得的数的和最大为多少。
(i, j) 为 i 行 j 列的单元格,根据 i + j 的奇偶性将节点分为两个集合,在矩阵中相邻得节点分别在图的两侧,得一个二分图。
s 点向左侧节点建边,容量为节点在矩阵中的值。
右侧节点向t 建边,容量为节点在矩阵中的值。
左侧节点向在矩阵中相邻的右侧节点建立容量为INF的边。
进入S集的右侧节点不取,进入T集的左侧节点不取,所得的最小割的值即为不取的数的和。
ans = sum - max_flow
左侧节点a和右侧节点b,如果在矩阵中相邻,则必须同时进入S集或T集,也就是说,a和b不能同时取得(如果同时进入S集则取a不取b)。因此建图时,a和b之间的边容量为INF,INF的边不能进入割集。
选择a和b进入S集或T集的过程是同过 比较a和b的权值 决策取舍a和b的过程。
#include
#include
#include
#include
#include
#include
#include
#define INF 0x3f3f3f3f
#define rep0(i, n) for (int i = 0; i < n; i++)
#define rep1(i, n) for (int i = 1; i <= n; i++)
#define rep_0(i, n) for (int i = n - 1; i >= 0; i--)
#define rep_1(i, n) for (int i = n; i > 0; i--)
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
#define mem(x, y) memset(x, y, sizeof(x))
#define MAXN 110
using namespace std;
typedef long long ll;
const int S = 0, T = MAXN * MAXN - 1;
struct Edge
{
int to, cap, flow, rev;
};
vector g[MAXN * MAXN];
int grid[MAXN][MAXN], m, n;
int idx(int i, int j)
{
return i * n + j + 1;
}
void addEdge(int u, int v, int cap)
{
g[u].push_back((Edge){v, cap, 0, g[v].size()});
g[v].push_back((Edge){u, 0, 0, g[u].size() - 1});
}
int level[MAXN * MAXN], itr[MAXN * MAXN];
queue q;
void bfs()
{
mem(level, -1);
q.push(S);
level[S] = 0;
while (!q.empty())
{
int u = q.front();
q.pop();
for (int i = 0; i < g[u].size(); i++)
{
Edge e = g[u][i];
if (level[e.to] < 0 && e.cap > e.flow)
{
level[e.to] = level[u] + 1;
q.push(e.to);
}
}
}
}
int dfs(int u, int f)
{
if (u == T)
return f;
for (int& i = itr[u]; i < g[u].size(); i++)
{
Edge& e = g[u][i];
if (level[e.to] > level[u] && e.cap > e.flow)
{
int d = dfs(e.to, MIN(f, e.cap - e.flow));
if (d)
{
e.flow += d;
g[e.to][e.rev].flow -= d;
return d;
}
}
}
return 0;
}
ll max_flow()
{
ll flow = 0;
while (true)
{
bfs();
if (level[T] < 0)
return flow;
ll f;
mem(itr, 0);
while ((f = dfs(S, INF)) > 0)
{
flow += f;
}
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
#endif // ONLINE_JUDGE
ll ans = 0;
scanf("%d %d", &m, &n);
for (int i = 0; i < m; i++)
for (int j = 0; j < n; j++)
{
scanf("%d", &grid[i][j]);
ans += grid[i][j];
}
for (int i = 0; i < m; i++)
{
for (int j = 0; j < n; j++)
{
if ((i + j) & 1)
{
addEdge(idx(i, j), T, grid[i][j]);
if (i - 1 >= 0)
addEdge(idx(i - 1, j), idx(i, j), INF);
if (j - 1 >= 0)
addEdge(idx(i, j - 1), idx(i, j), INF);
}
else
{
addEdge(S, idx(i, j), grid[i][j]);
if (i - 1 >= 0)
addEdge(idx(i, j), idx(i - 1, j), INF);
if (j - 1 >= 0)
addEdge(idx(i, j), idx(i, j - 1), INF);
}
}
}
ans -= max_flow();
printf("%lld\n", ans);
return 0;
}