【最大流】[SCOI2007]蜥蜴 BZOJ 1066

[SCOI2007]蜥蜴 BZOJ 1066

Time Limit: 1 Sec  Memory Limit:162 MB

Description

在一个r行c列的网格地图中有一些高度不同的石柱,一些石柱上站着一些蜥蜴,你的任务是让尽量多的蜥蜴逃到边界外。 每行每列中相邻石柱的距离为1,蜥蜴的跳跃距离是d,即蜥蜴可以跳到平面距离不超过d的任何一个石柱上。石柱都不稳定,每次当蜥蜴跳跃时,所离开的石柱高度减1(如果仍然落在地图内部,则到达的石柱高度不变),如果该石柱原来高度为1,则蜥蜴离开后消失。以后其他蜥蜴不能落脚。任何时刻不能有两只蜥蜴在同一个石柱上。

Input

输入第一行为三个整数r,c,d,即地图的规模与最大跳跃距离。以下r行为石竹的初始状态,0表示没有石柱,1~3表示石柱的初始高度。以下r行为蜥蜴位置,“L”表示蜥蜴,“.”表示没有蜥蜴。

Output

输出仅一行,包含一个整数,即无法逃离的蜥蜴总数的最小值。

Sample Input

5 8 2
00000000
02000000
00321100
02000000
00000000
........
........
..LLLL..
........
........

Sample Output

1

HINT

100%的数据满足:1<=r, c<=20, 1<=d<=3





说实话,没有经验还真看不出来

不过一旦看出来了就恍然大悟了,每个点拆成两个点(上下),可以理解成石柱上部和下部,然后从上部到下部连一条边,权值为高度(很巧妙)

然后源点和有蜥蜴的点连边,正无穷,然后边缘可以出去的石柱和汇点连边,正无穷

可以互相到达的石柱连边,正无穷

应该很好理解(想出来了以后)


然后跑一次sap(仍然是优化后的),ok



测评情况(BZOJ)



C++ AC Code

/*http://blog.csdn.net/jiangzh7
By Jiangzh*/
#include<cstdio>
#include<cmath>
#define min(a,b) ((a)<(b)?(a):(b))
const int N=20+10;
const int NN=N*N;
const int inf=0x3f3f3f3f;
int n,m,d,lizard_tot;
int height[N][N],lizard[N][N];
int g[NN][NN],S,T;
int v[NN],h[NN];

inline int IN(int x,int y){return (x*m+y)*2+1;}
inline int OU(int x,int y){return (x*m+y)*2+2;}

void read()
{
	scanf("%d%d%d",&n,&m,&d);
	char s[100];
	for(int i=0;i<n;i++)
	{
		scanf("%s",s);
		for(int j=0;j<m;j++) 
			height[i][j]=s[j]-'0';
	}
	for(int i=0;i<n;i++)
	{
		scanf("%s",s);
		for(int j=0;j<m;j++)
		{
			lizard[i][j]=(s[j]=='L');
			if(lizard[i][j]) lizard_tot++;
		}
	}
}

inline void inlink(int x,int y,int flow) { g[x][y]=flow; }
inline int dis(int x1,int y1,int x2,int y2)
{
	return (x1-x2)*(x1-x2)+(y1-y2)*(y1-y2);
}

void build_map()
{
	S=OU(n-1,m-1)+1; T=OU(n-1,m-1)+2;
	for(int i=0;i<n;i++)
		for(int j=0;j<m;j++)
		{
			if(lizard[i][j]) inlink(S,IN(i,j),1);
			if(height[i][j])
			{
				inlink(IN(i,j),OU(i,j),height[i][j]);
				if(i<d||i>=n-d||j<d||j>=m-d)
					inlink(OU(i,j),T,inf);
				for(int x=0;x<n;x++)
					for(int y=0;y<m;y++)
					{
						if(i==x && j==y) continue;
						if(!height[x][y]) continue;
						if(dis(i,j,x,y)<=d*d)
							inlink(OU(i,j),IN(x,y),inf);
					}
			}
		}
}

int sap(int x,int flow)
{
	if(x==T) return flow;
	int res=0;
	for(int i=0;i<=T;i++)
		if(g[x][i] && h[x]==h[i]+1)
		{
			int t=sap(i,min(g[x][i],flow-res));
			g[x][i]-=t; g[i][x]+=t;
			if((res+=t)==flow) return res;
			if(h[S]>=T) return res;
		}
	if(!(--v[h[x]])) h[S]=T;
	++v[++h[x]];
	return res;
}

void work()
{
	build_map();
	int res=0;
	v[S]=T;
	while(h[S]<T) res+=sap(S,inf);
	printf("%d\n",lizard_tot-res);
}

int main()
{
	freopen("lizard.in","r",stdin);
	freopen("lizard.out","w",stdout);
	read();
	work();
	return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值