链接
https://www.luogu.org/problemnew/show/P2774
大意
给定一个 n×m n × m 矩阵,先要取出若干个元素使得剩下的元素周围都没有元素,求求出的元素的最大和
思路
最大流求最小割
给定样例
3 2
1 3
5 2
4 6
其编号为
1 2
3 4
5 6
我们把这
n×m
n
×
m
个点分成两部分,变成一张二分图,然后给其建立权值(为自己本身),如下图
然后,对周围的点两两连边,容量为无穷大,但不能比
S
S
<script type="math/tex" id="MathJax-Element-3">S</script>的容量大
再跑一遍网络流即可
代码
#include<cstring>
#include<cstdio>
#include<queue>
#define N 10001
#define M 80001
#define k(i,j) (i-1)*m+j
#define min(a,b) a<b?a:b
#define max(a,b) a>b?a:b
using namespace std;int id,f,n,m,ans,d[N],l[M],s,t,sum,a;char c;
int read()
{
char c;f=0;
while(c=getchar(),c<=47||c>=58);f=(f<<3)+(f<<1)+c-48;
while(c=getchar(),c>=48&&c<=57) f=(f<<3)+(f<<1)+c-48;
return f;
}
struct node{int next,to,w;}e[M];int tot;
const short dx[4]={-1,0,1,0};
const short dy[4]={0,1,0,-1};
void add(int u,int v,int w)
{
e[tot].to=v;e[tot].w=w;e[tot].next=l[u];l[u]=tot++;
e[tot].to=u;e[tot].w=0;e[tot].next=l[v];l[v]=tot++;
return;
}
bool bfs()
{
memset(d,-1,sizeof(d));
queue<int>q;
q.push(s);d[s]=0;
while(q.size())
{
int x=q.front();q.pop();
for(int i=l[x];i!=-1;i=e[i].next)
{
int y=e[i].to;
if(e[i].w&&d[y]==-1)
{
d[y]=d[x]+1;
q.push(y);
if(y==t) return true;
}
}
}
return false;
}
int dfs(int x,int flow)
{
if(x==t||!flow) return flow;
int rest=0,f;
for(int i=l[x];i!=-1;i=e[i].next)
{
int y=e[i].to;
if(d[x]+1==d[y]&&e[i].w)
{
f=dfs(y,min(flow-rest,e[i].w));
e[i].w-=f;rest+=f;e[i^1].w+=f;
if(flow==rest) return rest;
}
}
if(!rest) d[x]=-1;
return rest;
}
int dinic()
{
int r=0;
while(bfs()) r+=dfs(s,1e9);
return r;
}
int main()
{
memset(l,-1,sizeof(l));
n=read();m=read();s=n*m+1;t=s+1;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
sum+=(a=read());//总量
id=k(i,j);
if((i+j)&1) add(s,id,a);else add(id,t,a);//与源点或者汇点建图
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if((i+j)&1)
for(int o=0;o<4;o++)
{
int nx=i+dx[o],ny=j+dy[o];
if(nx<1||ny<1||nx>n||ny>m) continue;
id=k(nx,ny);
add(k(i,j),id,1e8);//和周围的点建图
}
printf("%d",sum-dinic());//输出
}