[SCOI2007] 蜥蜴

题目描述

在一个 \(r\)\(c\) 列的网格地图中有一些高度不同的石柱,第 \(i\)\(j\) 列的石柱高度为 \(h_{i,j}\)

一些石柱上站着一些蜥蜴,你的任务是让尽量多的蜥蜴逃到边界外。

每行每列中相邻石柱的距离为 \(1\),蜥蜴的跳跃距离是 \(d\),即蜥蜴可以跳到平面距离不超过 \(d\) 的任何一个石柱上。

石柱都不稳定,每次当蜥蜴跳跃时,所离开的石柱高度减 \(1\)(如果仍然落在地图内部,则到达的石柱高度不变)。

如果该石柱原来高度为 \(1\),则蜥蜴离开后消失,以后其他蜥蜴不能落脚。

任何时刻不能有两只蜥蜴在同一个石柱上。

对于 \(100\%\) 的数据满足:\(1\le r,c\le20\)\(1\le d\le 4\)\(1\le h\le 3\)

题目分析

最近在做网络流的题,也是因为考试遇到了,所以学了一下网络流。但是很菜,也就只会板子了。

学了之后发现,只要知道拆点这个方法,这道题就很好想了。

因为 限制条件:通过次数 是在点上,所以把每个值不为零的点分配两个编号,一个入点,一个出点。柱子高度为零的点忽略不管。

  1. 入点到出点连边,容量为这个点柱子高度,即可通过的蜥蜴数。

  2. 然后每两个可以到达的点的出点都与对方的入点连边,容量正无穷。

  3. 每个可以到达地图外的点与汇点连边,容量正无穷。

  4. 原点到每个有蜥蜴的点连边,容量为 1 。

这样图的模型就建好了,只需要再跑一个最大流就解决了。得到的最大流就是可以逃离的蜥蜴数,用总量减一下就是答案了。

Code

点击查看代码
#include<bits/stdc++.h>
using namespace std;

const int N=4005,M=4e5,INF=0x3f3f3f3f;
int nxt[M],h[N],ver[M],w[M],co=1;
int de[N],no[N*2];
int n,m,d,s,t;
int mp[25][25],id[25][25],c=1;

inline int read(){
	int sum=0,f=1;char a=getchar();
	while(a<'0' || a>'9'){if(a=='-') 	f=-1;a=getchar();}
	while(a>='0' && a<='9') 	sum=sum*10+a-'0',a=getchar();
	return sum*f;
}

void add(int x,int y,int z){
	nxt[++co]=h[x],h[x]=co,ver[co]=y,w[co]=z;
	nxt[++co]=h[y],h[y]=co,ver[co]=x,w[co]=0;
}

bool ch(int x,int y,int x1,int y1){
	int t1=abs(x-x1),t2=abs(y-y1);
	double tmp=sqrt(1.0*t1*t1+1.0*t2*t2);
	if(tmp<=d)	return 1;
	return 0;
}

bool bfs(){
	memset(de,0,sizeof de);
	queue<int> q;
	q.push(s);
	de[s]=1,no[s]=h[s];
	while(q.size()){
		int x=q.front();
		q.pop();
		for(int i=h[x];i;i=nxt[i]){
			int y=ver[i];
			if(w[i] && !de[y]){
				q.push(y);
				no[y]=h[y];
				de[y]=de[x]+1;
				if(y==t)	return 1;
			}
		}
	}
	return 0;
}
int dfs(int x,int flow){
	if(x==t)	return flow;
	int rest=flow,k,i;
	for(i=h[x];i && rest;i=nxt[i]){
		int y=ver[i];
		if(de[y]==de[x]+1 && w[i]){
			k=dfs(y,min(rest,w[i]));
			if(!k)	de[y]=0;
			w[i]-=k;
			w[i^1]+=k;
			rest-=k;
		}
	}
	no[x]=i;
	return flow-rest;
}

int main(){
	
	char a;int tot=0;
	n=read(),m=read(),d=read();
	memset(mp,0x3f,sizeof mp);
	for(int i=1;i<=n;++i)
		for(int j=1;j<=m;++j)	cin>>a,mp[i][j]=a-'0';

	for(int i=1;i<=n;++i)
		for(int j=1;j<=m;++j)
			if(mp[i][j]){
				id[i][j]=++c;++c;
				add(id[i][j],c,mp[i][j]);
			}

	s=++c,t=++c;

	memset(de,0,sizeof de);
	for(int x=0;x<=n;++x)
		for(int y=0;y<=m;++y)
			for(int x1=1;x1<=n+1;++x1)
				for(int y1=1;y1<=m+1;++y1)
					if((x!=x1 || y!=y1) && mp[x][y] && mp[x1][y1] && ch(x,y,x1,y1)){
						if((!x || !y) && (x1==n+1 || y==m+1))	continue;
						if(!x || !y){
							if(!de[id[x1][y1]]){de[id[x1][y1]]=1;add(id[x1][y1]^1,t,INF);}
						}
						else if(x1==n+1 || y1==m+1){
							if(!de[id[x][y]]){de[id[x][y]]=1;add(id[x][y]^1,t,INF);}
						}
						else
							add(id[x][y]^1,id[x1][y1],INF);
					}

	for(int i=1;i<=n;++i)
		for(int j=1;j<=m;++j){
			cin>>a;
			if(a=='L')	add(s,id[i][j],1),++tot;
		}
	
	int ans=0,flow=0;
	while(bfs()){
		while(flow=dfs(s,INF))	ans+=flow;
	}
	
	cout<<tot-ans;

	return 0;
}

结语

这道题思路其实不难,但是建图的代码实现有些细节要自己慢慢调。

多交几次,或者去 BZOJ 白嫖数据也可以

posted @ 2022-07-10 20:44  _yolanda  阅读(40)  评论(0编辑  收藏  举报