P2472 [SCOI2007] 蜥蜴

题目链接:link

题面:

P2472 [SCOI2007] 蜥蜴

题目描述

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

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

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

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

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

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

输入格式

输入格式

输入第一行为三个整数 \(r,c,d\),即地图的规模与最大跳跃距离。

接下来 \(r\) 行每行 \(c\) 个数字为石柱的初始状态,\(0\) 表示没有石柱,否则表示石柱的初始高度 \(h_{i,j}\)

接下来 \(r\) 行每行 \(c\) 个字符为蜥蜴位置,L 表示蜥蜴,. 表示没有蜥蜴。

输出格式

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

输入输出样例 #1

输入 #1

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

输出 #1

1

说明/提示

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

思路:

把一个石柱分为顶点和底点两个点,两点间的容量为石柱的高度。设置一个超级源点和超级汇点。接下来考虑连边:

  1. 超级源点向每个有蜥蜴石柱的底点连一个容量为1的边。

  2. 能逃出的石柱的顶点向超级汇点连一条容量为inf的边。

  3. 在距离小于d的两个石柱连一条容量为inf的边,连接其中一个石柱的底点和另一个石柱的顶点。

tips:所有反边val值为 0 !

其他细节见代码注释叭 ~

#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
const int N=10005,inf=1e9;
int m,n,d,k,cnt=1,tot,s,t,ans,head[N],dis[N],cur[N],h[1005][1005],x[N],y[N],id[1005][1005];
char ch[205];queue<int> q;
struct node{int to,nxt,val;}e[N<<1];
inline void add(int x,int y,int z){
	e[++cnt].to=y;
	e[cnt].val=z;
	e[cnt].nxt=head[x];
	head[x]=cnt;
}//建边就不用多说了吧 
inline bool bfs(int s,int t){
	memset(dis,0x3f,sizeof(dis));
	while(!q.empty()) q.pop();//清空 
	for(int i=0;i<=2*k+1;i++) cur[i]=head[i];//当前弧优化 
	dis[s]=0;
	q.push(s);
	while(!q.empty()){
		int x=q.front();q.pop();
		for(int i=head[x];i;i=e[i].nxt){
			int y=e[i].to;
			if(dis[y]>inf&&e[i].val){//未被遍历过且有剩余流量 
				dis[y]=dis[x]+1;//更新距离 
				q.push(y);
			}
		}
	}
	if(dis[t]<inf) return true;//能到汇点 即还有剩余网络 
	else return false;
}
inline int dfs(int x,int t,int limit){
	if(!limit||x==t) return limit;//到达汇点或没有剩余流量 
	int flow=0,f;
	for(int i=cur[x];i;i=e[i].nxt){
		cur[x]=i;
		int y=e[i].to;//当前弧优化 
		if(dis[y]==dis[x]+1&&e[i].val&&(f=dfs(y,t,min(limit,e[i].val)))){//流量最小值 
			flow+=f;//最大流增加 
			limit-=f;//剩余流减少 
			e[i].val-=f;//此条边的流量减少 
			e[i^1].val+=f;//反向边的流量增加 
			if(!limit) break;//没有剩余流量 
		}
	}
	return flow;//返回答案 
}
int main(){
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	cin>>n>>m>>d;
	for(int i=1;i<=n;i++){
		cin>>(ch+1);
		for(int j=1;j<=m;j++){
			h[i][j]=ch[j]-48;//记录高度 
			if(h[i][j]){
				k++;//计录石柱数量 
				x[k]=i;y[k]=j;//记录石柱做标 
				id[i][j]=k;//石柱编号 
			}
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			if(h[i][j]&&(i<=d||j<=d||i+d>n||j+d>m)){//能跳出去 
				add(id[i][j]+k,2*k+1,inf);add(2*k+1,id[i][j]+k,0);//向2 ×k + 1(汇点)连边 
			}
		}
	}
	for(int i=1;i<=n;i++){
		cin>>(ch+1);
		for(int j=1;j<=m;j++){
			if(ch[j]=='L'){//有蜥蜴 
				tot++;//记录蜥蜴数量 
				add(0,id[i][j],1);add(id[i][j],0,0);//连超级源点(0) 
			}
		}
	}
	for(int i=1;i<=k;i++){
		add(i,i+k,h[x[i]][y[i]]);//石柱的顶点与底点相连 
		add(i+k,i,0);
	}
	for(int i=1;i<=k;i++){
		for(int j=1;j<=k;j++){
			if(i==j) continue;
			if((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j])<=d*d){//距离小于d的石柱相连 
				add(i+k,j,inf);add(j,i+k,0);//顶点连底点 
			}
		}
	}
	s=0;t=2*k+1;//超级源点 and 超级汇点 
	while(bfs(s,t)) ans+=dfs(s,t,inf);//据说是网络流板子 
	cout<<tot-ans<<'\n';//总数减去能逃走的数量就是答案 
	return 0;
}

借鉴自大佬链接

后言:蒟蒻实力有限,欢迎各位巨佬指出错误~

posted @ 2025-08-15 11:12  晏清玖安  阅读(21)  评论(0)    收藏  举报