1. 程式人生 > 其它 >【洛谷】P2598 [ZJOI2009]狼和羊的故事(最小割)

【洛谷】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;
}