【洛谷】P2598 [ZJOI2009]狼和羊的故事(最小割)

原题链接

题意

给定一张 \(n \times m\) 的网格图,每个点可能是狼的领地,也可能是羊的领地,或者两者皆不是。要求在一些点的边上建栅栏,使得所有狼的领地和羊的领地无法直接相连。求最小建的栅栏数。

数据范围

\(1 \leq n,m \leq 100\)

思路

注意到题目要求羊的领地和狼的领地不连通,又要代价最小,可以联系到网络流中的最小割模型。

首先,从源点向所有羊的领地连一条流量为 \(+\infty\) 的边,从所有狼的领地向汇点连一条流量为 \(+\infty\)。这样可以保证在最小割中的割边不会与源点或者汇点相连,在本题中的含义就是不会在不存在的地方建栅栏。再从所有原图中的点向四周连一条流量为 \(1\) 的点,表示如果在这两个点中建栅栏,代价为 \(1\)

这样网络流模型就建好了。再根据最大流最小割定理,求出网络中的最大流就是本题的答案。

code:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e5+10;
const int M=2e5+10;
const int INF=0x3f3f3f3f;
int dx[4]={-1,1,0,0};
int dy[4]={0,0,-1,1};
int h[N],idx=1,q[N],d[N],s,t,cur[N],n,m;
struct edge{
	int v,w,nex;
}e[M];
void add(int u,int v,int w){e[++idx].v=v;e[idx].w=w;e[idx].nex=h[u];h[u]=idx;}
bool bfs()
{
	int hh=0,tt=-1;memset(d,-1,sizeof(d));d[s]=0,cur[s]=h[s];q[++tt]=s;
	while(hh<=tt)
	{
		int u=q[hh++];
		for(int i=h[u];i;i=e[i].nex)
		{
			int v=e[i].v;
			if(d[v]==-1&&e[i].w)
			{
				d[v]=d[u]+1;cur[v]=h[v];
				if(v==t) return true;
				q[++tt]=v;
			}
		} 
	} 
	return false;
}
int dfs(int u,int limit)
{
	if(u==t) return limit;
	int flow=0;
	for(int i=cur[u];i&&flow<limit;i=e[i].nex)
	{
		int v=e[i].v;cur[u]=i;
		if(d[v]==d[u]+1&&e[i].w)
		{
			int t=dfs(v,min(e[i].w,limit-flow));
			if(!t) d[v]=-1;flow+=t;e[i].w-=t,e[i^1].w+=t;
		}
	}
	return flow;
}
int dinic()
{
	int res=0,flow;
	while(bfs()) while(flow=dfs(s,INF)) res+=flow;
	return res;
}
int get(int i,int j){return (i-1)*m+j;}
int main()
{
	scanf("%d%d",&n,&m);s=0,t=n*m+1;
	for(int i=1;i<=n;i++)
	    for(int j=1;j<=m;j++)
	    {
	    	int opt,u=get(i,j),v;scanf("%d",&opt);
	    	if(opt==1) add(s,u,INF),add(u,s,0);
			if(opt==2) add(u,t,INF),add(t,u,0);
			for(int k=0;k<4;k++)
			{
				int x=i+dx[k],y=j+dy[k];
				if(x<1||x>n||y<1||y>m) continue;
				v=get(x,y);add(u,v,1),add(v,u,0);
			}
		}
	printf("%d\n",dinic());
	return 0;
} 
posted @ 2021-10-28 11:10  曙诚  阅读(66)  评论(0)    收藏  举报