Dili_iiii

编码全要靠底力

【网络流/最小割】方格取数问题

题目描述
在一个有 m*n 个方格的棋盘中,每个方格中有一个正整数。现要从方格中取数,使任意 2 个数所在方格没有公共边,且取出的数的总和最大。试设计一个满足要求的取数算法。对于给定的方格棋盘,按照取数要求编程找出总和最大的数。

输入格式
第 1 行有 2 个正整数 m 和 n,分别表示棋盘的行数和列数。接下来的 m 行,每行有 n 个正整数,表示棋盘方格中的数。

对于相邻的方格,横纵坐标之和奇偶性不同,那么将其分成两部分,形成二分图,求最小割即踢掉不符合条件的最小的数和,奇偶两部分分别连汇点和源点,边权为方格内的数,而两部分之间的边权取INF,保证割掉的是两个点之一的边权。

#pragma GCC optimize(3)
#include<bits/stdc++.h>
#define ll long long 
const int maxn=1e4+5;
const int maxm=1e5+5;
const int INF=0x3f3f3f3f;
using namespace std;
struct node
{
	int ver[maxm],nex[maxm],f[maxm],head[maxn],dep[maxn],cur[maxn];
	int src,sink,tot,n;
	ll ant;
	void add(int u,int v,int flow)
	{
		ver[++tot]=v;
		nex[tot]=head[u];
		f[tot]=flow;
		head[u]=tot;
		ver[++tot]=u;
		nex[tot]=head[v];
		f[tot]=0;
		head[v]=tot;
	}
	bool bfs()
	{
		queue<int> q;
		memset(dep,-1,sizeof dep);
		dep[src]=0;
		q.push(src);
		while(!q.empty())
		{
			int u=q.front();
			q.pop();
			for(int i=head[u];i;i=nex[i])
			{
				int v=ver[i];
				if(dep[v]==-1&&f[i])
				{
					dep[v]=dep[u]+1;
					if(v==sink)
						return true;
					q.push(v);
				}
			}
		}
		return false;
	}
	int dfs(int u,int mx)
	{
		if(u==sink)
			return mx;
		for(int &i=cur[u];i;i=nex[i])
		{
			int v=ver[i];
			if(dep[v]==dep[u]+1&&f[i]>0)
			{
				int flow=dfs(v,min(mx,f[i]));
				if(flow)
				{
					f[i]-=flow;
					f[i^1]+=flow;
					return flow;
				}
			}
		}
		return 0;
	}
	ll dinic(int s,int t)
	{
		this->src=s;
		this->sink=t;
		ant=0;
		while(bfs())
		{
			for(int i=0;i<=n;i++)
			{
				cur[i]=head[i];
			}
			while(int d=dfs(src,INF))
			{
				ant+=d;
			}
		}
		return ant;
	}
	void init(int n)
	{
		this->n=n;
		memset(head,0,sizeof head);
		tot=1;
	}
}G;
int dx[4]={-1,1,0,0};
int dy[4]={0,0,-1,1};
int main() 
{
	int n,m,s,t;
	scanf("%d %d",&n,&m);
	s=n*m+1;
	t=n*m+2;
	ll ant=0;
	G.init(t+5);
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			int x;
			scanf("%d",&x);
			ant+=x;
			if((i+j)&1)
			{
				G.add(s,(i-1)*m+j,x);
			}
			else
			{
				G.add((i-1)*m+j,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 ni=i+dx[k];
					int nj=j+dy[k];
					if(ni<1||ni>n||nj<1||nj>m)
						continue;
					G.add((i-1)*m+j,(ni-1)*m+nj,INF);
				}
			}
		}
	}
	ant-=G.dinic(n*m+1,n*m+2);
	printf("%lld\n",ant);
	return 0;
}

posted @ 2019-10-21 21:28  Dili_iiii  阅读(151)  评论(0编辑  收藏  举报