Libre 6007 「网络流 24 题」方格取数 / Luogu 2774 方格取数问题 (网络流,最大流)

Libre 6007 「网络流 24 题」方格取数 / Luogu 2774 方格取数问题 (网络流,最大流)

Description

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

Input

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

Output

程序运行结束时,将取数的最大总和输出

Sample Input

3 3
1 2 3
3 2 3
2 3 1

Sample Output

11

Http

Libre:https://loj.ac/problem/6007
Luogu:https://www.luogu.org/problem/show?pid=2774

Source

网络流,最大流

解决思路

总觉的这道题能想到用网络流解很玄学
网络流的另外一种模型:点覆盖模型,就是有诸如两个只能选一个的限制这类,没有做过的话根本想不到。
我们把网格黑白染色,让黑格周围都是白格,白格同理,简单点来说对于格子(i,j),若i%2==j%2则黑色,否则白色。
另外建立一个源点一个汇点。对于每一个黑色的格子,从源点连一条容量为格子上的数的边,并且向周围的所有相邻白格子连一条容量为无穷大的边。对于每一个白格子,从白格子连一条容量位格子上的数的边到汇点。
同时,统计所有格子上的数之和 记为sum。求出最大流flow,则答案就是flow-sum。
这种解法的原理是基于最小割最大流,由于笔者知识水平不高,暂且不知道如何证明。但可以通过画图手动模拟的方式基本上可以认识到其正确性,即通过巧妙的方式割去其中部分边点,使得剩下的最大(笔者也只能理解到这里了)
另:这里使用Dinic实现最大流,具体可以移步我的这篇文章

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;

#define pos(x,y) (x-1)*n+y

const int maxN=40;
const int inf=2147483647;

class Edge
{
public:
	int u,v,flow;
};

int n,m;
int cnt=-1;
int Mat[maxN][maxN];
int Head[maxN*maxN];
int Next[maxN*maxN*maxN*maxN];
Edge E[maxN*maxN*maxN*maxN];
int depth[maxN*maxN];
int cur[maxN*maxN];
int Q[maxN*maxN*maxN];

void Add_Edge(int u,int v,int flow);
bool bfs();
int dfs(int u,int flow);

int main()
{
	memset(Head,-1,sizeof(Head));
	int sum=0;
	scanf("%d%d",&m,&n);
	for (int i=1;i<=m;i++)
		for (int j=1;j<=n;j++)
		{
			int num;
			scanf("%d",&num);
			sum+=num;//sum统计所有格子的权之和
			if (i%2==j%2)//若为黑格子
			{
				Add_Edge(0,pos(i,j),num);//连接源点
				if (i!=1)//分别连接四周的白格子,注意判断存在与否
					Add_Edge(pos(i,j),pos(i-1,j),inf);
				if (i!=m)
					Add_Edge(pos(i,j),pos(i+1,j),inf);
				if (j!=1)
					Add_Edge(pos(i,j),pos(i,j-1),inf);
				if (j!=n)
					Add_Edge(pos(i,j),pos(i,j+1),inf);
			}
			else
				Add_Edge(pos(i,j),n*m+1,num);//若为白格子,则只连接汇点
		}
	int Ans=0;
	while (bfs())//求解最大流
	{
	    memcpy(cur,Head,sizeof(cur));
		while (int di=dfs(0,inf))
			Ans+=di;
	}
	cout<<sum-Ans<<endl;
	return 0;
}

void Add_Edge(int u,int v,int flow)
{
	cnt++;
	Next[cnt]=Head[u];
	Head[u]=cnt;
	E[cnt].u=u;
	E[cnt].v=v;
    E[cnt].flow=flow;

	cnt++;
	Next[cnt]=Head[v];
	Head[v]=cnt;
	E[cnt].u=v;
	E[cnt].v=u;
	E[cnt].flow=0;
}

bool bfs()
{
	memset(depth,-1,sizeof(depth));
	int h=1,t=0;
	Q[1]=0;
	depth[0]=1;
	do
	{
		t++;
		int u=Q[t];
		for (int i=Head[u];i!=-1;i=Next[i])
		{
			int v=E[i].v;
			if ((E[i].flow>0)&&(depth[v]==-1))
			{
				h++;
				Q[h]=v;
				depth[v]=depth[u]+1;
			}
		}
	}
	while (t!=h);
	if (depth[n*m+1]==-1)
		return 0;
	return 1;
}

int dfs(int u,int flow)
{
	if (u==n*m+1)
		return flow;
	for (int i=Head[u];i!=-1;i=Next[i])
	{
		int v=E[i].v;
		if ((E[i].flow>0)&&(depth[v]==depth[u]+1))
		{
			int di=dfs(v,min(flow,E[i].flow));
			if (di>0)
			{
				E[i].flow-=di;
				E[i^1].flow+=di;
				return di;
			}
		}
	}
	return 0;
}
原文地址:https://www.cnblogs.com/SYCstudio/p/7286914.html