中文题干
就是说让你在一个方格格里面取数字,然后保证取出的数字和最大。
又是一道让我日常感叹,这也是网络流的题目。
刚开始又以为是一个数学问题。
不说废话,直接说为什么这是一道网络流的题目。
想了挺久的,最后我这样说服自己了,这道题目的意思就是,我们要从一堆数字中拿出一些数字,保证数字的和是最大的,这些数和数之间是有相互的制约关系的。那我们就把他从方格里面抽象出来,一些点,相互制约。如果我们把点权想成边权呢?我们要找一些边,使得这些边的权值之和最大,同时边与边之间有一些制约关系。这不就成了网络流里面的最小割问题了吗?
嗯??? 就成了最小割问题了。好像是的吧。 那怎么建图呢:
首先我们先给棋盘进行染色,黑白相间的那种,为的就是能够明确的分出点与点之间的制约关系。假设第一个格子的点为白色。我们把白色的格子和源点相连接,将黑色的格子和汇点相连接。以上的两种边,流量都是自己点的权值。然后再将相邻的白色格子和黑色格子相连接,流量是无穷大。
这样我们跑完一遍之后,断掉的就是最小的那条割。
上面给出图,此图来自洛谷题解
#include <iostream>
#include <cstring>
#include <queue>
#include <cstdio>
#define INF 0x3f3f3f3f
#define len 100010
using namespace std;
struct edge{
int u, v, nxt, f;
};
edge e[len];
int head[len], hcnt;
int dep[len], cur[len];
int S, T;
int M[110][110], color[110][110];
int dc[4][2] = {{1, 0}, {0, 1}, {0, -1}, {-1, 0}};
void Init (){
memset (head, -1, sizeof (head));
memset(cur, 0, sizeof (cur));
memset(M, 0, sizeof (M));
hcnt = 0;
}
void adde(int u, int v, int f){
e[hcnt].u = u; e[hcnt].v = v; e[hcnt].f = f;
e[hcnt].nxt = head[u]; head[u] = hcnt ++;
e[hcnt].u = v; e[hcnt].v = u; e[hcnt].f = 0;
e[hcnt].nxt = head[v]; head[v] = hcnt ++;
}
bool bfs(){
queue <int> q;
memset(dep, -1, sizeof (dep));
dep[S] = 0; q.push(S);
while (!q.empty()){
int h = q.front(); q.pop();
for (int i=head[h]; ~i; i=e[i].nxt){
int v = e[i].v;
if (dep[v] == -1 && e[i].f > 0){
dep[v] = dep[h] + 1;
q.push(v);
if (v == T) return true;
}
}
}
return false;
}
int dfs(int x, int mw){
if (x == T || !mw) return mw;
int sum = 0;
for (int i=cur[x]; ~i; i=e[i].nxt){
cur[x] = i;
int v = e[i].v;
if (dep[v] == dep[x]+1 && e[i].f > 0){
int flow = dfs(v, min(e[i].f, mw));
if (flow){
sum += flow;
mw -= flow;
e[i].f -= flow;
e[i^1].f += flow;
if (!mw) break;
}
}
}
if (!sum) dep[x] = -1;
return sum;
}
int dinic(){
int ans = 0;
while (bfs()){
for (int i=S; i<=T; i++) cur[i] = head[i];
ans += dfs(S, INF);
}
return ans;
}
int main(){
int n, m; scanf("%d%d", &n, &m);
int res = 0;
Init();
S = 0; T = n * m + 1;
for (int i=1; i<=n; i++){
for (int j=1; j<=m; j++){
scanf("%d", &M[i][j]);
res += M[i][j];
}
}
for (int i=1; i<=n; i++){
for (int j=1; j<=m; j++){
if (j == 1 && i == 1) color[i][j] = 1;
else if (j == 1) color[i][j] = 1- color[i-1][j];
else color[i][j] = 1 - color[i][j-1];
}
}//染色
for (int i=1; i<=n; i++){
for (int j=1; j<=m; j++){
if (color[i][j]) adde(S, (i-1)*m+j, M[i][j]);
else adde((i-1)*m+j, T, M[i][j]);
if (color[i][j]){
for (int k=0; k<4; k++){
int _x = dc[k][0] + i;
int _y = dc[k][1] + j;
if (_x<1 || _x>n || _y<1 || _y>m) continue;
adde((i-1)*m+j, (_x-1)*m+_y, INF);
}
}
}
}
int ans = dinic();
printf ("%d\n", res - ans);
return 0;
}