【ZJOI 2009】狼和羊的故事

本文介绍了一个使用网络流算法解决羊狼合养问题的案例,通过构造特殊的网络流模型,实现了羊狼分离的同时确保了各自领地的完整性,最小化了额外篱笆的建设长度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【题目】

传送门

题目描述:

“ “ 狼爱上羊啊爱的疯狂,谁让他们真爱了一场;狼爱上羊啊并不荒唐,他们说有爱就有方向...... ” ”

Orez 听到这首歌,心想:狼和羊如此和谐,为什么不尝试羊狼合养呢?说干就干!

Orez 的羊狼圈可以看作一个 n × m n\times m n×m 个矩阵格子,这个矩阵的边缘已经装上了篱笆。可是 Drake 很快发现狼再怎么也是狼,它们总是对羊垂涎三尺,那首歌只不过是一个动人的传说而已。所以 Orez 决定在羊狼圈中再加入一些篱笆,还是要将羊狼分开来养。

通过仔细观察,Orez发现狼和羊都有属于自己领地,若狼和羊们不能呆在自己的领地,那它们就会变得非常暴躁,不利于他们的成长。

Orez 想要添加篱笆的尽可能的短。当然这个篱笆首先得保证不能改变狼羊的所属领地,再就是篱笆必须修筑完整,也就是说必须修建在单位格子的边界上并且不能只修建一部分。

输入格式:

文件的第一行包含两个整数 n n n m m m。接下来 n n n 行每行 m m m 个整数, 1 1 1 表示该格子属于狼的领地, 2 2 2 表示属于羊的领地, 0 0 0 表示该格子不是任何一只动物的领地。

输出格式:

文件中仅包含一个整数 a n s ans ans,代表篱笆的最短长度。

样例数据:

输入
2 2
2 2
1 1

输出
2

数据范围:

10 % 10\% 10% 的数据, n , m ≤ 3 n,m≤3 n,m3
30 % 30\% 30% 的数据 , n , m ≤ 20 n,m≤20 n,m20
100 % 100\% 100% 的数据, n , m ≤ 100 n,m≤100 n,m100


【分析】

妙啊,完全没想到网络流

从源点向每只羊连边,从每只狼向汇点连边,然后每个格子向四周连边

由于狼和羊要隔开,就相当于求一个割,由于又是最小的割,跑一遍最大流就可以了


【代码】

#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 10005
#define M 1000005
#define inf (1ll<<31ll)-1
using namespace std;
int n,m,s,t,tot=1;
int v[M],w[M],nxt[M];
int f[N],d[N],first[N];
int tx[5]={0,0,0,1,-1},ty[5]={0,1,-1,0,0};
int id(int i,int j){return (i-1)*m+j;}
void add(int x,int y,int f)
{
	nxt[++tot]=first[x];
	first[x]=tot,v[tot]=y,w[tot]=f;
}
bool bfs(int s)
{
	int x,y,i;
	memset(d,-1,sizeof(d));
	memcpy(f,first,sizeof(f));
	queue<int>q;q.push(s);d[s]=0;
	while(!q.empty())
	{
		x=q.front();q.pop();
		for(i=f[x];i;i=nxt[i])
		{
			y=v[i];
			if(w[i]&&d[y]==-1)
			{
				d[y]=d[x]+1;
				q.push(y);
			}
		}
	}
	return d[t]!=-1;
}
int dinic(int now,int flow)
{
	if(now==t)  return flow;
	int x,ans=0,delta;
	for(int &i=f[now];i;i=nxt[i])
	{
		x=v[i];
		if(d[x]==d[now]+1&&w[i])
		{
			delta=dinic(x,min(flow,w[i]));
			w[i]-=delta,w[i^1]+=delta;
			flow-=delta,ans+=delta;
			if(!flow)  return ans;
		}
	}
	return ans;
}
int main()
{
	int i,j,k,x,ans=0;
	scanf("%d%d",&n,&m);
	s=0,t=n*m+1;
	for(i=1;i<=n;++i){
		for(j=1;j<=m;++j){
			scanf("%d",&x);
			if(x==1)  add(s,id(i,j),inf),add(id(i,j),s,0);
			if(x==2)  add(id(i,j),t,inf),add(t,id(i,j),0);
		}
	}
	for(i=1;i<=n;++i){
		for(j=1;j<=m;++j){
			for(k=1;k<=4;++k){
				int x=i+tx[k];
				int y=j+ty[k];
				if(x>=1&&x<=n&&y>=1&&y<=m)
				  add(id(i,j),id(x,y),1),add(id(x,y),id(i,j),0);
			}
		}
	}
	while(bfs(s))  ans+=dinic(s,inf);
	printf("%d",ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值