【luogu 2774】方格取数 (最小割)
【题目大意】
有n*m的方格,在其中取任意个格子的数,保证最终结果最大,取得数字不能相邻
【题目思路】
如果不是知道是网络流的题,大概会试下爆搜,取之后周围的不可取之类的,但是显而易见会T
然后考虑怎么用网络流做,为什么能用网络流做
我自己对于网络流的理解,是解决选择之间的直接冲突,从而得到最优解的方法,而这个题每一个数字选和不选之间就会发生冲突
【建图】
如何实现选择
拆点,这里大多数程序都是将点根据(i+j)分成两类,我觉得是为了代码更简洁些,如果把所有点都拆成两个的话还是可行的
连边,将一类点和起点相连,一类点和终点相连,权值为对应点权值,将互相矛盾的点互相连接,权值为INF,
跑最大流/最小割即可,此处,对已有的图求割的结果,两边的点互相不连通,产生了使选择的点之间绝对不矛盾的最小权值
这里割掉的一定是和源汇点相邻的,而要断绝的目的就是令矛盾的点之间失去连接,所以最终最小割表示的就是不取的点的权值和
用总权值减去最小割即可
#include<cstdio>
#include<queue>
#include<cstring>
#define ll long long
using namespace std;
const int MAXN = 5010;
const int MAXM = 200010;
const ll INF = (1ll << 31) - 1;
ll sum = 0;
struct note
{
int to;
int nt;
int rev;
ll cal;
};
int nxt[4][2] = { {0,1},{1,0},{-1,0},{0,-1} };
struct edge
{
note arr[MAXM];
int siz;
int maxn;
int a[MAXN];
int dp[MAXN];
int st[MAXN];
int dis[MAXN];
int cur[MAXN];
int depth[MAXN];
int top;
int n, m, s, t;
edge()
{
memset(st, -1, sizeof(st));
memset(depth, -1, sizeof(depth));
memset(dis, -1, sizeof(dis));
top = 0;
}
void read()
{
scanf("%d%d", &n, &m);
s = 0, t = siz = n * m + 1;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
int id;
ll x;
scanf("%lld", &x);
sum += x;
id = (i - 1) * m + j;
if ((i + j) & 1)
add( s,id, x);
else
add(id, t, x);
}
}
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
if ((i + j) & 1)
{
for (int k = 0; k < 4; k++)
{
int nx = i + nxt[k][0], ny = j + nxt[k][1],id;
if (nx <= 0 || nx > n || ny <= 0 || ny > m) continue;
id = (i - 1) * m + j;
add(id, (nx - 1) * m + ny, INF);
}
}
}
}
}
bool dep()
{
queue<int> q;
q.push(s);
memset(depth, -1, sizeof(depth));
depth[s] = 0;
while (!q.empty())
{
int v = q.front(); q.pop();
for (int i = st[v]; i != -1; i = arr[i].nt)
{
int to = arr[i].to;
if (!arr[i].cal)
continue;
if (depth[to] != -1)
continue;
depth[to] = depth[v] + 1;
q.push(to);
}
}
return (depth[t] != -1);
}
void add(int x, int y, ll z)
{
top++; arr[top] = { y,st[x],top + 1,z }; st[x] = top;
top++; arr[top] = { x,st[y],top - 1,0 }; st[y] = top;
}
ll dfs(int now, ll val)
{
if (now == t || !val)
return val;
ll flow = 0;
for (int& i = cur[now]; i != -1; i = arr[i].nt)
{
int to = arr[i].to;
if (depth[to] != depth[now] + 1)
continue;
ll f = dfs(to, min(arr[i].cal, val));
if (!f || !arr[i].cal)
continue;
flow += f;
arr[i].cal -= f;
arr[arr[i].rev].cal += f;
val -= f;
if (!val)
return flow;
}
return flow;
}
ll dinic()
{
ll flow = 0;
ll f;
while (dep())
{
for (int i = 0; i <= siz; i++)
cur[i] = st[i];
while (f = dfs(s, INF))
flow += f;
}
return flow;
}
};
edge road, tmp;
void print(ll ans)
{
printf("%lld", ans);
}
int main()
{
road.read();
// printf("%lld \n", sum);
ll ans = sum - road.dinic();
print(ans);
return 0;
}