似乎是非常经典的一道题
说白了就是二分图带权最大独立集
看到这道题的第一眼就想到了正确做法
然而一直不知道如何证明
黑白染色成二分图
源点向黑点连边,权值为格子上的数
白点向汇点连边,权值为格子上的数
黑点向相邻的白点连边,权值无穷
独立集与覆盖集互补,因而最大权独立集与最小权覆盖集互补
又对于覆盖集中的任意一个点,按照上述方法建图时,删去其与源/汇相连的边,就相当于删去了其在二分图中覆盖的边
因而最小权覆盖集等价于最小割,大致如此
即最大权独立集=总权值-最小权覆盖集=总权值-最小割=总权值-最大流,Dinic即可
代码如下:
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
inline int read() {
int X=0,w=0;
char ch=0;
while(!isdigit(ch)) {
w|=ch=='-';
ch=getchar();
}
while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
return w?-X:X;
}
struct edge {
int v,l,p;
} e[400010];
int n,m,top=1,head[1000],cur[1000],a[910],d[1000];
inline void addedge(int u,int v,int l) {
e[++top]=(edge) {v,l,head[u]};
head[u]=top;
e[++top]=(edge) {u,0,head[v]};
head[v]=top;
}
bool bfs() {
memset(d,0,sizeof(d));
queue<int> q;
d[n*m+1]=1;
q.push(n*m+1);
while(!q.empty()) {
int f=q.front();
for(int i=head[f]; i; i=e[i].p) {
if((!d[e[i].v])&&e[i].l) {
d[e[i].v]=d[f]+1;
q.push(e[i].v);
}
}
q.pop();
}
return d[n*m+2];
}
int dfs(int u,int delta) {
if(u==n*m+2) {
return delta;
}
int ret=0;
for(int &i=cur[u]; delta&&i; i=e[i].p) {
if((d[e[i].v]==d[u]+1)&&e[i].l) {
int dd=dfs(e[i].v,min(e[i].l,delta));
e[i].l-=dd;
e[i^1].l+=dd;
delta-=dd;
ret+=dd;
}
}
if(!ret)cur[u]=0;
return ret;
}
int Dinic() {
int ret=0;
while(bfs()) {
memcpy(cur,head,sizeof(cur));
ret+=dfs(n*m+1,INF);
}
return ret;
}
int main() {
freopen("grid.in","r",stdin);
freopen("grid.out","w",stdout);
cin>>n>>m;
int sum=0;
for(int i=1; i<=n*m; i++) {
cin>>a[i];
sum+=a[i];
}
for(int i=1; i<=n*m; i++) {
if(((i-1)%m+(i-1)/m)%2==0) {
addedge(n*m+1,i,a[i]);
if(i%m)addedge(i,i+1,INF);
if(i%m!=1)addedge(i,i-1,INF);
if(i>m)addedge(i,i-m,INF);
if(i<=(n-1)*m)addedge(i,i+m,INF);
} else {
addedge(i,n*m+2,a[i]);
}
}
cout<<sum-Dinic()<<endl;
}