[BZOJ 1066][SCOI2007]蜥蜴
Description
在一个r行c列的网格地图中有一些高度不同的石柱,一些石柱上站着一些蜥蜴,你的任务是让尽量多的蜥蜴逃到边界外。 每行每列中相邻石柱的距离为1,蜥蜴的跳跃距离是d,即蜥蜴能够跳到平面距离不超过d的不论什么一个石柱上。石柱都不稳定,每次当蜥蜴跳跃时,所离开的石柱高度减1(假设仍然落在地图内部,则到达的石柱高度不变),假设该石柱原来高度为1,则蜥蜴离开后消失。以后其它蜥蜴不能落脚。不论什么时刻不能有两仅仅蜥蜴在同一个石柱上。
Input
输入第一行为三个整数r,c,d,即地图的规模与最大跳跃距离。下面r行为石竹的初始状态,0表示没有石柱,1~3表示石柱的初始高度。下面r行为蜥蜴位置,“L”表示蜥蜴,“.”表示没有蜥蜴。
Output
输出仅一行,包括一个整数,即无法逃离的蜥蜴总数的最小值。
Sample Input
00000000
02000000
00321100
02000000
00000000
........
........
..LLLL..
........
........
Sample Output
HINT
100%的数据满足:1<=r, c<=20, 1<=d<=3
Source
此题出得不错,是一道经典的网络流题,由题意得,图中每一个结点(即石柱)都有容量,依据《训练指南》的提示,能够将每一个结点拆成两个点(暂且称入点和出点),两个点间的弧上容量等于原来的结点容量(即石柱高度,石柱上能经过的蜥蜴数等于石柱高度,每有一个蜥蜴经过此石柱,石柱高度-1,石柱相应的弧残量-1,后面能经过该石柱的蜥蜴数-1),再建立一个起点s和终点t,每一个有蜥蜴的石柱相应的入点就和起点s连接,s与入点间的弧容量为1(含义是该石柱仅仅能有一个蜥蜴,假设容量大于1,就有可能在跑最大流时出现一个石柱有多个蜥蜴的情况),每一个与边界距离足够近的石柱相应的出点和终点t连接,t与出点间的弧容量为+∞,这样t与出点间的弧容量不会影响到结果,此题的图就建立完毕了,再跑一遍最大流,最大流等于能逃脱的蜥蜴数量,输出时用总蜥蜴数减去能逃脱的蜥蜴数就可以
我的思路是參考http://blog.csdn.net/jiangshibiao/article/details/20074193的做法,以下是他的题解中的图
#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; }