目录
模型:Floof Fill指洪水覆盖算法他,通常对在图中需要找到一片区域的连通块,用最简单的例子,例如求图中的连通块数量。
如何实现?
实现方式为对图中的每一个点进行bfs或者是dfs,但是这里不是遍历到每一个点都可以走的。在前面已经遍历过的点中的dfs会遍历掉所有符合条件的点,这个时候可能当前遍历到的点就被遍历了使得下一次不用遍历
(题目均截图来自于Acwing)
经典模板题:
1、池塘数量
思路:分析题意,我们只要按照前面的思路遍历一遍所有点,那么统计一下就可以了,而此处的判段条件则是为当前点是否是‘W’即代表雨水点
实现代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1010;
char g[N][N];
int n,m;
bool st[N][N];//判段当前点是否走过
int dir[8][2]={{0,1},{1,1},{1,0},{1,-1},{0,-1},{-1,-1},{-1,0},{-1,1}};//8个方向
void dfs(int x,int y){
if(st[x][y]) return;
st[x][y]=true;
for(int i=0;i<8;i++){
int tox=x+dir[i][0];
int toy=y+dir[i][1];
//判段是否出界,判段下一个走的点是不是W
if(tox>=0&&toy>=0&&tox<n&&toy<m&&g[tox][toy]=='W'){
dfs(tox,toy);
}
}
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
cin>>g[i][j];
}
}//输入操作
int count=0;//统计池塘数量
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
//判段是否被前面的dfs走过了或者直接不是W
if(!st[i][j]&&g[i][j]=='W'){
count++;
dfs(i,j);
}
}
}
cout<<count;
return 0;
}
备注:此处解释应该已经比较详细,这里用的dfs写,后面都会用y总讲的bfs做题解。另,这些图的问题通常涉及到往8个方向走和往4个方向走的差别,而这次我直接用了数组,但是其实用两层循环走更方便,就不用设数组
2、城堡问题
思路:这道题的输入带点问题,例如输入为11,我们可以考虑将11转化成2进制为1101,按照题意,则为西北南均有墙,这道题为4个方向的问题,所以我们在遍历的时候直接将遍历的顺序设为西北东南,即左上右下,这样的话,用当前数往右移i位&1就可以得到这个数在二进制中第i位的位置,而与方向相同,例如i为2时11的第2位为0,且i为2是往右走,所以走的时候直接判断这个方向是否有门即可,将可以走的路联通在一起,即为答案,这里还要维护一下最大的房间
实现代码:
#include<bits/stdc++.h>
using namespace std;
#define x first
#define y second
typedef pair<int,int> PII;
const int N=100;
int n,m;
int g[N][N];
PII q[N*N];
bool st[N][N];
int bfs(int x,int y){
int dir[4][2]={{0,-1},{-1,0},{0,1},{1,0}};//左上右下
int hh=0,tt=-1;
q[++tt]={x,y};//用数组模拟队列
int area=0;
st[x][y]=true;
while(hh<=tt){
PII t=q[hh++];
area++;
for(int i=0;i<4;i++){
int tox=t.x+dir[i][0];
int toy=t.y+dir[i][1];
//判段边界
if(tox<0||toy<0||tox>=n||toy>=m) continue;
if(st[tox][toy]) continue;
//判段i方向是否有门
if(g[t.x][t.y]>>i&1) continue;
q[++tt]={tox,toy};
st[tox][toy]=true;
}
}
return area;
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
cin>>g[i][j];
}
}
int cnt=0,area=0;//数量和面积
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
if(!st[i][j]){
area=max(area,bfs(i,j));//维护最大面积
cnt++;
}
}
}
cout<<cnt<<endl;
cout<<area<<endl;
return 0;
}
3、山峰和山谷
思路:同样我们对每个点进行bfs,但是这里不是所有的联通快都要记录,所以不能直接在bfs返回条件上做判段,我们可以在传参的时候添加一个地址传递,如果这块值相同的区域,周边没有比他更高或者更矮的才计入
实现代码:
#include<bits/stdc++.h>
using namespace std;
#define x first
#define y second
typedef pair<int,int> PII;
const int N =1010;
int n,g[N][N];
bool st[N][N];
PII q[N*N];
void bfs(int x,int y,bool &has_lower,bool &has_higher){
int dir[8][2]={{0,1},{1,1},{1,0},{1,-1},{0,-1},{-1,-1},{-1,0},{-1,1}};
int hh=0,tt=-1;
q[++tt]={x,y};
st[x][y]=true;
while(hh<=tt){
auto t=q[hh++];
for(int i=0;i<8;i++){
int tox=t.x+dir[i][0];
int toy=t.y+dir[i][1];
//判段边界情况
if(tox<0||toy<0||tox>=n||toy>=n) continue;
//如果不相等就更新更高和更矮的情况
if(g[tox][toy]!=g[x][y]){
if(g[tox][toy]>g[x][y]) has_higher=true;
else has_lower=true;
}else if(!st[tox][toy]){
q[++tt]={tox,toy};
st[tox][toy]=true;
//更新当前点进队列
}
}
}
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n;
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
cin>>g[i][j];
}
}
int peek=0,valley=0;
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
if(!st[i][j]){
//记录周围是否有比这块区域更高或者更矮的
bool has_lower=false,has_higher=false;
bfs(i,j,has_lower,has_higher);
if(!has_higher) peek++;
if(!has_lower) valley++;
}
}
}
cout<<peek<<" "<<valley<<endl;
return 0;
}