[BZOJ 1066][SCOI2007]蜥蜴

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

Source

Pku 2711 Leapin' Lizards

 

此题出得不错,是一道经典的网络流题,由题意得,图中每个结点(即石柱)都有容量,根据《训练指南》的提示,可以将每个结点拆成两个点(暂且称入点和出点),两个点间的弧上容量等于原来的结点容量(即石柱高度,石柱上能经过的蜥蜴数等于石柱高度,每有一个蜥蜴经过此石柱,石柱高度-1,石柱对应的弧残量-1,后面能经过该石柱的蜥蜴数-1),再建立一个起点s和终点t,每个有蜥蜴的石柱对应的入点就和起点s连接,s与入点间的弧容量为1(含义是该石柱只能有一个蜥蜴,如果容量大于1,就有可能在跑最大流时出现一个石柱有多个蜥蜴的情况),每个与边界距离足够近的石柱对应的出点和终点t连接,t与出点间的弧容量为+∞,这样t与出点间的弧容量不会影响到结果,此题的图就建立完成了,再跑一遍最大流,最大流等于能逃脱的蜥蜴数量,输出时用总蜥蜴数减去能逃脱的蜥蜴数即可

我的思路是参考http://blog.csdn.net/jiangshibiao/article/details/20074193的做法,下面是他的题解中的图

[BZOJ 1066][SCOI2007]蜥蜴_第1张图片

#include <stdio.h>
#include <string.h>
#include <queue>
#define MAXN 1050
#define MAXQ 20050
#define INF 999999
using namespace std;
int r,c,d,cnt=0,acnt=0,ans=0; //cnt=网络中的点的个数(个数=石柱个数*2,一个石柱对应2个点),acnt=蜥蜴个数,ans=可以离开边界的蜥蜴个数
int col[21][21]; //map数组保存网络,map[i][i+1]=第i个石柱的高度(点i->点i+1的容量),col[i][j]表示(i,j)的石柱编号
char in[21][21],ani[21][21]; //in[i][j]保存(i,j)有无石柱,ani[i][j]保存(i,j)有无蜥蜴
int map[MAXN][MAXN];
int pre[MAXQ],q[MAXQ]; //pre[i]=点i的前一个点
int min(int a,int b)
{
	if(a<b) return a;
	return b;
}
void makePic(int x,int y) //在(x,y)与能从(x,y)跳到的边之间建一条无穷大边
{
	int i,j,k;
	int now=col[x][y]+1; //now=该石柱对应的出点编号
	for(i=1;i<=r;i++)
		for(j=1;j<=c;j++)
		{
			if((i!=x||j!=y)&&(in[i][j]>'0')) //(i,j)是另外一个点,且(i,j)是石柱
				if(d*d>=(x-i)*(x-i)+(y-j)*(y-j)) //(i,j)和(x,y)间距离小于最大跳跃距离
					map[now][col[i][j]]=INF; //在(i,j)的出点和(x,y)的入点间建立一条无穷大边
		}
}
void goOut() //将图中所有能跳出去的蜥蜴跳出,更新图
{
	int i,j;
	for(i=1;i<=r;i++)
		for(j=1;j<=c;j++)
			if(i-d<1||i+d>r||j-d<1||j+d>c) //(i,j)与边界的最短距离小于跳跃距离
			{
				int now=col[i][j]+1; //now=该石柱对应的出点编号
				map[now][cnt]=INF; //从该石柱对应出点到汇点间建一条无穷大的边,表明可从该石柱跳到边界外
			}
}
void bfs_maxFlow() //bfs求最大流,数组模拟队列
{
	while(1)
	{
		int i,j,head=0,tail=1;
		q[1]=0;
		memset(pre,-1,sizeof(pre));
		while(head<tail)
		{
			int now=q[++head]; //now=队首,队首出队
			for(i=1;i<=cnt;i++)
				if(pre[i]<0&&map[now][i]>0) //该点没有扩展过,且i和当前点now联通
				{
					q[++tail]=i; //与now连接的下一个结点入队
					pre[i]=now;
				}
			if(pre[cnt]>0) break;
		}
		if(pre[cnt]<0) break; //找不到增广路,退出
		int minFlow=INF; //最小残留流量
		for(i=cnt;i!=0;i=pre[i])
			minFlow=min(minFlow,map[pre[i]][i]); //更新最小残留容量
		for(i=cnt;i!=0;i=pre[i])
		{
			//更新流量、残留容量
			map[pre[i]][i]-=minFlow;
			map[i][pre[i]]+=minFlow;
		}
		ans+=minFlow;
	}
}
void init() //输入数据
{
	int i,j,k;
	scanf("%ld%ld%ld",&r,&c,&d);
	for(i=1;i<=r;i++)
	{
		scanf("%s",in[i]+1);
		for(j=1;j<=c;j++)
		{
			if(in[i][j]!='0') //该点有石柱
			{
				cnt++; //石柱个数+1
				col[i][j]=cnt;
				map[cnt][cnt+1]=in[i][j]-48;
				cnt++;
			}
		}
	}
	for(i=1;i<=r;i++)
	{
		scanf("%s",ani[i]+1);
		for(j=1;j<=c;j++)
		{
			if(ani[i][j]=='L') //(i,j)有蜥蜴
			{
				acnt++;
				map[0][col[i][j]]=1;
			}
			if(in[i][j]>'0')
				makePic(i,j); //建图
		}
	}
}
int main()
{
	init(); //输入数据
	cnt++;
	goOut();
	bfs_maxFlow();
	printf("%ld",acnt-ans);
	return 0;
}


 

你可能感兴趣的:(图论,网络流)