BZOJ 1412 [ZJOI2009]狼和羊的故事 最小割

Description

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

Input

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

Output

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

Sample Input

2 2
2 2
1 1

Sample Output

2

数据范围
10%的数据 n,m≤3
30%的数据 n,m≤20
100%的数据 n,m≤100

HINT

Source


题目意思比较明确,其实说白了就是一个最小割问题。
我们要求1和2分居,事实上,可以看作1和2的最小割。
我们知最小割=最大流。
首先相邻的(上下左右)点连边,容量是1.
注意一下点的编号,一个n*m的矩形,每一个点都可以唯一表示。
如点(x,y)-->m*(x-1)+y,这个都知道把。
接着source(源点)和所有1连边,所有2和sink(汇点)连边。
接着就是处理最小割的过程。

也就是最大流了。。
用的是ISAP。

#include<bits/stdc++.h>
using namespace std;
int read(){
    int x=0,f=1;char ch=getchar();
    while (ch<'0' || ch>'9'){if (ch=='-') f=-1;ch=getchar();}
    while (ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
const int 
	inf=100,
	MAX=105,
	EdgM=70000;
const int 
	dx[4]={-1,0,1,0},
	dy[4]={0,-1,0,1};
int n,m,Ecnt;
int source,sink,Ver;
int gap[EdgM],Q[EdgM],pre[MAX*MAX],d[MAX*MAX];
char Map[MAX][MAX];
struct Edge{
	int next,to,C;
}E[EdgM]; int head[MAX*MAX],cur[MAX*MAX];
int vertex(int i,int j){
	return (i-1)*m+j;
}
void add(int u,int v,int w){
	E[Ecnt].next=head[u];
	E[Ecnt].to=v;
	E[Ecnt].C=w;
	head[u]=Ecnt++;
}
void build(){
	source=n*m+1,sink=n*m+2;
	Ecnt=0;
	memset(head,255,sizeof(head));
	for (int i=1;i<=n;i++)
		for (int j=1;j<=m;j++)	Map[i][j]=read();
	for (int i=1;i<=n;i++)
		for (int j=1;j<=m;j++){
			int now=vertex(i,j),now1;
			if (Map[i][j]==1)	add(source,now,inf),add(now,source,0);
			if (Map[i][j]==2){
				add(now,sink,inf),add(sink,now,0);
				continue;
			}
			for (int k=0;k<4;k++){
				int x1=dx[k]+i,y1=dy[k]+j;
				if (!x1 || !y1 || x1>n || y1>m) continue;
				if (Map[i][j]!=1 || Map[x1][y1]!=1)
					now1=vertex(x1,y1),add(now,now1,1),add(now1,now,0);
			}
		}
	Ver=n*m+2;
}
void BFS(){
	memset(gap,0,sizeof(gap));
	memset(d,255,sizeof(d));
	d[sink]=0; gap[0]++;
	int Head=0,tail=1;
	Q[0]=sink;
	while (Head!=tail){
		int u=Q[Head++];
		for (int i=head[u];~i;i=E[i].next){
			int j=E[i].to;
			if (~d[j]) continue;
			d[j]=d[u]+1;
			gap[d[j]]++;
			Q[tail++]=j;
		}
	}
}
int ISAP(){
	BFS();
	memcpy(cur,head,sizeof(cur));
	int u,flow=0;
	u=pre[source]=source;
	while (d[sink]<Ver+1){
		if (u==sink){
			int f=inf,neck;
			for (int i=source;i!=sink;i=E[cur[i]].to)
				if (f>E[cur[i]].C) f=E[cur[i]].C,neck=i;
			for (int i=source;i!=sink;i=E[cur[i]].to)
				E[cur[i]].C-=f,E[cur[i]^1].C+=f;
			flow+=f; u=neck;
		}
		int j;
		for (j=cur[u];~j;j=E[j].next)
			if (E[j].C && d[E[j].to]+1==d[u]) break;
		if (~j){
			pre[E[j].to]=u;
			cur[u]=j;
			u=E[j].to;
		}	else{
			if (!(--gap[d[u]])) break;
			int mind=Ver+1;
			for (int i=head[u];~i;i=E[i].next)
				if (E[i].C && mind>d[E[i].to])
					cur[u]=i,mind=d[E[i].to];
			d[u]=mind+1;
			gap[d[u]]++;
			u=pre[u];
		}
	}
	return flow;
}
int main(){
	n=read(),m=read();
	build();
//	for (int i=0;i<Ecnt;i++)
//		cout<<i<<' '<<E[i].to<<' '<<E[i].next<<' '<<E[i].C<<endl;
	printf("%d\n",ISAP());
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值