洛谷P2598 狼和羊的故事

题目描述

“狼爱上羊啊爱的疯狂,谁让他们真爱了一场;狼爱上羊啊并不荒唐,他们说有爱就有方向......” Orez听到这首歌,心想:狼和羊如此和谐,为什么不尝试羊狼合养呢?说干就干! Orez的羊狼圈可以看作一个n*m个矩阵格子,这个矩阵的边缘已经装上了篱笆。可是Drake很快发现狼再怎么也是狼,它们总是对羊垂涎三尺,那首歌只不过是一个动人的传说而已。所以Orez决定在羊狼圈中再加入一些篱笆,还是要将羊狼分开来养。 通过仔细观察,Orez发现狼和羊都有属于自己领地,若狼和羊们不能呆在自己的领地,那它们就会变得非常暴躁,不利于他们的成长。 Orez想要添加篱笆的尽可能的短。当然这个篱笆首先得保证不能改变狼羊的所属领地,再就是篱笆必须修筑完整,也就是说必须修建在单位格子的边界上并且不能只修建一部分。

输入输出格式

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

输出格式:文件中仅包含一个整数ans,代表篱笆的最短长度。

解析:一看就是最小割.

源点->狼(inf)

狼->羊或空(1)

空->狼或羊(1)

羊->汇点(inf)

要注意的是为了控制流量方向,羊不能往狼上连。

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
#define inf 1e9
struct point
{
	int next;
	int to;
	int dis;
}e[1000001];
int n,m,num,s,t;
int h[21000],head[21000];
int X[6]={0,1,-1,0,0},Y[6]={0,0,0,1,-1};
int g[2010][2010];
int get(int x,int y)
{
	return (x-1)*m+y;
}
void add(int from,int to,int dis)
{
	e[num].next=head[from];
	e[num].dis=dis;
	e[num].to=to;
	head[from]=num++;
}
int dfs(int x,int dis)
{
	if(x==t)
		return dis;
	int sum=0;
	for(int i=head[x];i!=0;i=e[i].next)
	{
		int to=e[i].to;
		if(h[to]==h[x]+1&&e[i].dis)
		{
			int diss=dfs(to,min(dis,e[i].dis));
			if(diss>0)
			{
				sum+=diss;
				dis-=diss;
				e[i].dis-=diss;
				e[i^1].dis+=diss;
				if(!sum)
					break;
			}
		}
	}
	if(!sum)
		h[x]=-1;
	return sum;
}
bool bfs()
{
	memset(h,0,sizeof(h));
	h[s]=1;
	queue<int>q;
	q.push(s);
	while(!q.empty())
	{
		int x=q.front();
		q.pop();
		for(int i=head[x];i!=0;i=e[i].next)
		{
			int to=e[i].to;
			if(!h[to]&&e[i].dis)
			{
				q.push(to);
				h[to]=h[x]+1;
			}
		}
	}
	return h[t];
}
int dinic()
{
	int tot=0;
	while(bfs())
		tot+=dfs(s,inf);
	return tot;
}
int main()
{
	cin>>n>>m;
	t=n*m+1;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
		{
			cin>>g[i][j];
			int x=get(i,j);
			if(g[i][j]==1)
			{
				add(x,0,0);
				add(0,x,inf);
			}
			if(g[i][j]==2)
			{
				add(t,x,0);
				add(x,t,inf);
			}
		}
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
		{
			for(int k=1;k<=4;k++)
			{
				int x=i+X[k];
				int y=j+Y[k];	
				if(x<=0||y<=0||x>n||y>m)
					continue;
				if(g[i][j]==2)
					continue;
				int xx=get(i,j);
				int yy=get(x,y);
				add(yy,xx,0);
				add(xx,yy,1);
			}
		}
	cout<<dinic();
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值