CF1920F1 Smooth Sailing (Easy Version) 题解

文章讲述了在一个给定的二维地图上,解决关于岛屿包围路径的问题,通过多源最短路计算每个点到最近火山的距离,然后使用二分查找和暴力检查策略来确定从询问点出发的合法路径的最大权值,其中涉及空间优化和暴力遍历技巧。
摘要由CSDN通过智能技术生成

魔幻暴力题。

题意简述

给你一张 n × m n\times m n×m 的地图,每个点是海 .,岛屿 # 或者火山 v。保证岛屿和非岛屿均可以形成恰好一个四连通块且岛屿不与地图边界接壤,至少有一个岛屿点与一个火山点。

定义一条合法的路径为,从一个非岛屿的点
s s s 出发,每次向四联通的非岛屿点走一格,用航线包围岛屿一整圈后回到点
s s s。一条路径包围岛屿定义为,不存在一条从岛屿出发的八连通路径可以在不触及路径的前提下到达边界。一条路径的权值为途中经过的所有点到离其最近的火山的曼哈顿距离的最小值。

现有 q q q 次询问,每次给定一个点
( x , y ) (x,y) (x,y),你需要求出从 ( x , y ) (x,y) (x,y) 出发的合法路径的最大权值。

3 ≤ n , m ≤ 1 0 5 , 9 ≤ n m ≤ 3 × 1 0 5 , q ≤ 5 3\leq n,m\leq 10^5,9\leq nm\leq 3\times 10^5,q\leq 5 3n,m105,9nm3×105,q5

解题思路

求最小值的最大值,具有单调性,可以二分。难点在于如何 check。

先跑一遍多源最短路把每个点离其最近的火山的曼哈顿距离算出来,记为 d i s i , j dis_{i,j} disi,j。若当前二分出来的答案为 k k k,则我们从询问的点 ( x , y ) (x,y) (x,y) 出发,向四个方向走,只能走 d i s dis dis 值大于等于 k k k 的点,将它们打上标记。

如何判断是否将岛屿围起来?考虑从岛屿点出发,向周围八个方向走,如果能够走到边界且路径上不经过被标记的点,则岛屿没有被完全包围。这个套路在ABC219 E中也用到了。那题也是个暴力题

于是我们就做完了。注意空间的问题,我 vector 用的不太熟练,被卡了好几发。

代码示例

#include<bits/stdc++.h>
using namespace std;
int n,m,Q,stx,sty;
int dx[10]={0,0,1,-1,1,-1,-1,1},dy[10]={1,-1,0,0,1,-1,1,-1};
string s[100010];
vector<int> dis[100010];
vector<bool> Mark[100010],vis[100010];
struct node{
	int x,y;
};
queue<node> q;
void Debug_dis(){
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++) cout<<dis[i][j]<<" ";
		cout<<endl;
	}
}
void Debug_Mark_points(int k){
	cout<<"ans:"<<k<<endl;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++) cout<<Mark[i][j]<<" ";
		cout<<endl;
	}
}
void Get_dis(){
	while(!q.empty()) q.pop();
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			if(s[i][j]=='v') dis[i][j]=0,q.push({i,j});
		}
	}
	while(!q.empty()){
		node u=q.front();q.pop();
		for(int i=0;i<4;i++){
			int tx=u.x+dx[i],ty=u.y+dy[i];
			if(tx<1||tx>n||ty<1||ty>m) continue;
			if(dis[tx][ty]>dis[u.x][u.y]+1){
				dis[tx][ty]=dis[u.x][u.y]+1;
				q.push({tx,ty});
			}
		}
	}
}
void Mark_points(int k){
	for(int i=1;i<=n;i++) Mark[i]=vector<bool>(m+5,0);
	while(!q.empty()) q.pop();
	q.push({stx,sty});
	Mark[stx][sty]=1;
	while(!q.empty()){
		node u=q.front();q.pop();
		for(int i=0;i<4;i++){
			int tx=u.x+dx[i],ty=u.y+dy[i];
			if(s[tx][ty]=='#') continue;
			if(tx<1||tx>n||ty<1||ty>m||dis[tx][ty]<k||Mark[tx][ty]) continue;
			Mark[tx][ty]=1;
			q.push({tx,ty});
		}
	}
}
bool check(int k){
	if(dis[stx][sty]<k) return 0;
	Mark_points(k);
	//if(k<=20) Debug_Mark_points(k);
	for(int i=1;i<=n;i++) vis[i]=vector<bool>(m+5,0);
	while(!q.empty()) q.pop();
	int X=0,Y=0;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++) if(s[i][j]=='#') X=i,Y=j; 
	}
	vis[X][Y]=1;
	q.push({X,Y});
	while(!q.empty()){
		node u=q.front();q.pop();
		for(int i=0;i<8;i++){
			int tx=u.x+dx[i],ty=u.y+dy[i];
			if(tx<1||tx>n||ty<1||ty>m) return 0;
			if(Mark[tx][ty]||vis[tx][ty]) continue;
			vis[tx][ty]=1;
			q.push({tx,ty});
		}
	}
	return 1;
}
void C_close(){
	std::ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
}
int main(){
	C_close();
	cin>>n>>m>>Q;
	for(int i=1;i<=n;i++) cin>>s[i],s[i]=" "+s[i];
	for(int i=1;i<=n;i++) dis[i]=vector<int>(m+5,1e6);
	Get_dis();
	//Debug_dis();
	while(Q--){
		cin>>stx>>sty;
		int l=0,r=n+m+5,ans=0;
		while(l<=r){
			int mid=(l+r)>>1;
			if(check(mid)) l=mid+1,ans=mid;
			else r=mid-1;
		}
		cout<<ans<<endl;
	}
	return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值