题解-luogu-p2774方格取数问题

题目链接

不难发现,每个方格会与其上下左右四个方格产生矛盾。编程的任务即找到一种不产生矛盾的选择方案,并且使得取出的数总和最大。

首先对图进行黑白染色,目的是使产生矛盾的两个位置分别位于不同的色块中,方便建图。

源点与所有白色位置相连,权值为该位置上的数字;所有黑色位置与汇点相连,权值也为该位置上的数字;所有白色位置与其上下左右(注意边界情况)的黑色位置相连,权值为无穷大。

如此建图后,可以发现存在源点到汇点的增广路,这也意味着原图中存在产生矛盾的两个位置。假设一开始选取M*N网格中的所有方块,我们的任务是割掉网络中的一些边(即删去一些方块),使得割去的边权最小。割去网络中的边就相当于删掉两个矛盾位置中的其中一个,因此当网络中不再有源点到汇点的增广路,就意味着矛盾全部消除。

问题便转化为求解最小割(最大流)的问题。输出答案为全局和减去最小割。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
#include<bits/stdc++.h>
using namespace std;

typedef long long ll;

const int INF=0x3f3f3f3f;
const int MAXN=100005;

int N,M,S,T;
ll sum;

int nx[]={0,1,0,-1};
int ny[]={1,0,-1,0};

struct Edge{
int v,w,nxt;
}mem[MAXN];
int head[MAXN],cnt=1;

inline void AddEdge(int u,int v,int w){
mem[++cnt].w=w;
mem[cnt].v=v;
mem[cnt].nxt=head[u];
head[u]=cnt;
}

int d[MAXN];
bool vis[MAXN];

inline bool bfs(){
memset(vis,0,sizeof(vis));
vis[S]=1;d[S]=0;
queue<int> q;
q.push(S);
while(q.size()){
int u=q.front();q.pop();
for(register int i=head[u];i;i=mem[i].nxt){
int v=mem[i].v,w=mem[i].w;
if(vis[v]||(w==0)) continue;
vis[v]=1;d[v]=d[u]+1;
q.push(v);
}
}
return vis[T];
}

inline int dfs(int u,int flow){
if(u==T) return flow;
int rflow;
for(register int i=head[u];i;i=mem[i].nxt){
int v=mem[i].v,w=mem[i].w;
if(w==0||d[u]+1!=d[v]) continue;
if(rflow=dfs(v,min(flow,w))){
mem[i].w-=rflow;
mem[i^1].w+=rflow;
return rflow;
}
}
return 0;
}

inline int Dinic(){
int maxflow=0,lowflow;
while(bfs()){
while(lowflow=dfs(S,INF)) maxflow+=lowflow;
}
return maxflow;
}

int main(){
scanf("%d%d",&M,&N);
S=0,T=M*N+1;
for(register int i=1;i<=M;++i){
for(register int j=1;j<=N;++j){
int w;
scanf("%d",&w);
sum+=w;
if((i+j)&1){
AddEdge(S,(i-1)*N+j,w);
AddEdge((i-1)*N+j,S,INF);
for(register int k=0;k<=3;++k){
int tx=i+nx[k],ty=j+ny[k];
if(tx<1||tx>M||ty<1||ty>N) continue;
AddEdge((i-1)*N+j,(tx-1)*N+ty,INF);
AddEdge((tx-1)*N+ty,(i-1)*N+j,0);
}
}
else{
AddEdge((i-1)*N+j,T,w);
AddEdge(T,(i-1)*N+j,INF);
}
}
}
printf("%lld",sum-Dinic());
return 0;
}

Comments

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×