BZOJ 1412 最小割

    这道题的最小割模型比较明显,要求用最小的花费来使狼和羊不连通。那么我们可以将狼的领地与源点相连,羊的领地与汇点相连,流量均为正无穷。对于羊和狼相邻的格子,我们连一条流量为1的边,对于羊或狼与空地相邻的格子,我们连一条流量为1的边,表示这块空地可能属于狼或羊,对于空地和空地相邻的格子,我们也要连一条流量为1的边,因为狼和羊可能走到空地上相遇,如果出现这种情况我们也要割断这条边。最后跑一边最大流求出最小割即是答案。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define maxn 10005
#define maxm 100005
#define INF 1000000000
int last[maxn],pre[maxm],other[maxm],cap[maxm];
int n,m,a[104][104],l,q[maxm+5],d[maxn],S,T,ans;
int dx[5]={0,0,1,0,-1};
int dy[5]={0,1,0,-1,0};

void connect(int x,int y,int z)
{
	pre[l]=last[x];
	last[x]=l;
	other[l]=y;
	cap[l]=z;
	l++;
	swap(x,y);
	pre[l]=last[x];
	last[x]=l;
	other[l]=y;
	cap[l]=0;
	l++;	
}

bool bfs(void)
{
	memset(d,0,sizeof d);
	int h=0,t=1;
	q[1]=S;d[S]=1;
	while (h!=t) 
	{
		h=h%maxm+1;
		int u=q[h];
		for (int p=last[u];p!=-1;p=pre[p]) 
		{
			int v=other[p];
			if (d[v]||cap[p]==0) continue;
			d[v]=d[u]+1;
			if (v==T) return 1;
			t=t%maxm+1;
			q[t]=v;	
		}
	}
	return 0;
}

int dinic(int u,int flow)
{
	int rest=flow;
	if (u==T) return flow;
	for (int p=last[u];p!=-1;p=pre[p]) 
	{
		int v=other[p];
		if (rest>0&&d[v]==d[u]+1&&cap[p]>0) 
		{
			int temp=dinic(v,min(rest,cap[p]));
			rest-=temp;
			cap[p]-=temp;
			cap[p^1]+=temp;	
		}
	}
	return flow-rest;
}

int main()
{
	scanf("%d%d",&n,&m);
	memset(last,-1,sizeof last);
	S=0;T=n*m+1;
	for (int i=1;i<=n;i++)
		for (int j=1;j<=m;j++) 
		{
			scanf("%d",&a[i][j]);
			if (a[i][j]==1) connect(S,(i-1)*m+j,INF);
			if (a[i][j]==2) connect((i-1)*m+j,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+dx[k],y=j+dy[k];
				if (x>n||x<1||y>m||y<1) continue;
				if (a[i][j]==1) 
					if (a[i][j]!=a[x][y]) 
						connect((i-1)*m+j,(x-1)*m+y,1);
				if (a[i][j]==2) 
					if (a[x][y]==0) 
						connect((x-1)*m+y,(i-1)*m+j,1);	
				if (a[i][j]==a[x][y]&&a[i][j]==0) 
					connect((i-1)*m+j,(x-1)*m+y,1);
			}
		}
	while (bfs()) 
		ans+=dinic(S,INF);
	printf("%d\n",ans);
	return 0;	
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值