[Acwing提高]搜索
BFS的Flood Fill和最短路模型
知识点
题目 | 扩展方式 | 类型 |
---|---|---|
池塘计数 | 明确的二维地图 | Flood Fill |
城堡问题 | 地图转换,统计连通块数量 | Flood Fill |
山峰和山谷 | 搜索试探过程中记录一些东西,统计连通块内外分界处的关系 | Flood Fill |
迷宫问题 | 记录BFS最短路 | BFS最短路 |
武士风度的牛 | 走马字 | BFS最短路 |
抓住那头牛 | 一维最短路,估计范围 | BFS最短路 |
题目
池塘计数
思路
裸的Flood Fill
代码
#include<bits/stdc++.h>
using namespace std;
#define x first
#define y second
typedef pair<int,int>PII;
const int N=1010,M=N*N;
int n,m;
char g[N][N];
PII q[M];
bool st[N][N];
void bfs(int sx,int sy)
{
int hh=0,tt=0;
q[0]={sx,sy};
st[sx][sy]=1;
while(hh<=tt)
{
PII t=q[hh++];
for(int i=t.x-1;i<=t.x+1;i++)
for(int j=t.y-1;j<=t.y+1;j++)
{
if(i==t.x&&j==t.y)continue;
if(i<0||i>=n||j<0||j>=m||g[i][j]=='.'||st[i][j])continue;
q[++tt]={i,j};
st[i][j]=1;
}
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++)scanf("%s",g[i]);
int cnt=0;
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
if(g[i][j]=='W'&&!st[i][j])
{
bfs(i,j);
cnt++;
}
printf("%d\n",cnt);
return 0;
}
城堡问题
思路
没有直接给地图张啥样,给了上下左右是否有墙的二进制信息,不用直接转换,bfs内部用下面代码看某个方向有没有墙即可
i>>k&1
统计连通块面积只要统计出队次数即可
代码
#include<bits/stdc++.h>
#define x first
#define y second
using namespace std;
const int N=55,M=N*N;
typedef pair<int,int> PII;
int n,m;
int g[N][N];
PII q[M];
bool st[N][N];
int bfs(int sx,int sy)
{
int dx[]={0,-1,0,1},dy[]={-1,0,1,0};
int hh=0,tt=0;
q[0]={sx,sy};
int area=0;
st[sx][sy]=1;
while(hh<=tt)
{
PII t=q[hh++];
area++;
for(int i=0;i<4;i++)
{
int a=t.x+dx[i],b=t.y+dy[i];
if(a<0||a>=n||b<0||b>=m)continue;
if(st[a][b])continue;
if(g[t.x][t.y]>>i&1)continue;
q[++tt]={a,b};
st[a][b]=1;
}
}
return area;
}
int main()
{
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<<area<<endl;
return 0;
}
山峰和山谷
思路
统计边界信息,所以不能因为st访问过就直接continue。需要区分一下是连通块内部(st可以直接conitnue)还是连通块外部(不可以直接conitnue)
代码
#include<bits/stdc++.h>
#define x first
#define y second
using namespace std;
typedef pair<int,int>PII;
const int N=1e3+10,M=N*N;
PII q[M];
int g[N][N];
bool st[N][N];
int n;
int bfs(int sx,int sy)
{
int hh=0,tt=0;
int sf=1,sg=1;
q[0]={sx,sy};
st[sx][sy]=1;
while(hh<=tt)
{
PII t=q[hh++];
for(int i=t.x-1;i<=t.x+1;i++)
for(int j=t.y-1;j<=t.y+1;j++)
{
if(i==t.x&&j==t.y)continue;
if(i<0||j<0||i>=n||j>=n)continue;
if(g[i][j]>g[t.x][t.y])
{
sf=0;
continue;
}
if(g[i][j]<g[t.x][t.y])
{
sg=0;
continue;
}
if(g[i][j]==g[t.x][t.y]&&!st[i][j])
{
q[++tt]={i,j};
st[i][j]=1;
}
}
}
return sf+sg*2;
}
int main()
{
scanf("%d",&n);
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
scanf("%d",&g[i][j]);
int sf=0,sg=0;
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
if(!st[i][j])
{
int flag=bfs(i,j);
if(flag==1)sf++;
else if(flag==2)sg++;
else if(flag==3)sf++,sg++;
}
printf("%d %d",sf,sg);
return 0;
}
yxc的代码
#include <cstring>
#include <iostream>
#include <algorithm>
#define x first
#define y second
using namespace std;
typedef pair<int, int> PII;
const int N = 1010, M = N * N;
int n;
int h[N][N];
PII q[M];
bool st[N][N];
void bfs(int sx, int sy, bool& has_higher, bool& has_lower)
{
int hh = 0, tt = 0;
q[0] = {sx, sy};
st[sx][sy] = true;
while (hh <= tt)
{
PII t = q[hh ++ ];
for (int i = t.x - 1; i <= t.x + 1; i ++ )
for (int j = t.y - 1; j <= t.y + 1; j ++ )
{
if (i == t.x && j == t.y) continue;
if (i < 0 || i >= n || j < 0 || j >= n) continue;
if (h[i][j] != h[t.x][t.y]) // 山脉的边界
{
if (h[i][j] > h[t.x][t.y]) has_higher = true;
else has_lower = true;
}
else if (!st[i][j])
{
q[ ++ tt] = {i, j};
st[i][j] = true;
}
}
}
}
int main()
{
scanf("%d", &n);
for (int i = 0; i < n; i ++ )
for (int j = 0; j < n; j ++ )
scanf("%d", &h[i][j]);
int peak = 0, valley = 0;
for (int i = 0; i < n; i ++ )
for (int j = 0; j < n; j ++ )
if (!st[i][j])
{
bool has_higher = false, has_lower = false;
bfs(i, j, has_higher, has_lower);
if (!has_higher) peak ++ ;
if (!has_lower) valley ++ ;
}
printf("%d %d\n", peak, valley);
return 0;
}
作者:yxc
链接:https://www.acwing.com/activity/content/code/content/130795/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
迷宫问题
思路
输出最短路方案
把st数组改为PII类型的pre数组记录转移,为了方便输出避免逆序,我们从终点开始BFS到起点,pre数组同时起标记和记录转移的作用
代码
#include<bits/stdc++.h>
#define x first
#define y second
using namespace std;
typedef pair<int,int>PII;
const int N=1e3+10,M=N*N;
int n;
PII q[M];
int g[N][N];
PII pre[N][N];
void bfs(int sx,int sy)
{
int dx[]={0,1,0,-1},dy[]={1,0,-1,0};
q[0]={sx,sy};
int hh=0,tt=0;
memset(pre,-1,sizeof pre);
pre[sx][sy]={0,0};
while(hh<=tt)
{
PII t=q[hh++];
for(int i=0;i<4;i++)
{
int a=t.x+dx[i];
int b=t.y+dy[i];
if(a<0||b<0||a>=n||b>=n||pre[a][b].x!=-1||g[a][b])continue;
q[++tt]={a,b};
pre[a][b]=t;
}
}
}
int main()
{
scanf("%d",&n);
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
scanf("%d",&g[i][j]);
bfs(n-1,n-1);//逆序搜索
PII end(0,0);
while(1)
{
printf("%d %d\n",end.x,end.y);
if(end.x==n-1&&end.y==n-1)break;
end=pre[end.x][end.y];
}
return 0;
}
武士风度的牛
思路
比较裸的BFS求最短路,会写移动数组即可
其实这个BFS求最短路可以看成堆优化版本的dijkstra的特殊情况版本(优先队列边权不同有不一样的优先级,这个边权都相同那就普通队列嘛)
代码(结构体写法)
#include<bits/stdc++.h>
using namespace std;
const int N=200,M=N*N;
char g[N][N];
bool st[N][N];
struct Node
{
int x,y,s;
}q[M];
int n,m,cnt;
int sx,sy,ex,ey;
Node bfs(int sx,int sy)
{
int hh=0,tt=0;
int dx[]={2,2,1,-1,1,-1,-2,-2};
int dy[]={-1,1,-2,-2,2,2,-1,1};
q[0]={sx,sy,1};
st[sx][sy]=1;
while(hh<=tt)
{
Node t=q[hh++];
for(int i=0;i<8;i++)
{
int a=t.x+dx[i],b=t.y+dy[i];
if(a<0||b<0||a>=n||b>=m||g[a][b]=='*'||st[a][b])continue;
q[++tt]={a,b,t.s+1};
st[a][b]=1;
if(a==ex&&b==ey)return t;
}
}
}
int main()
{
scanf("%d%d",&m,&n);
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
{
cin>>g[i][j];
if(g[i][j]=='K')sx=i,sy=j;
if(g[i][j]=='H')ex=i,ey=j;
}
cout<<bfs(sx,sy).s;
return 0;
}
yxc的代码(开数组的写法,后期写图论最短路可能习惯写)
#include <cstring>
#include <iostream>
#include <algorithm>
#define x first
#define y second
using namespace std;
typedef pair<int, int> PII;
const int N = 155, M = N * N;
int n, m;
char g[N][N];
PII q[M];
int dist[N][N];
int bfs()
{
int dx[] = {-2, -1, 1, 2, 2, 1, -1, -2};
int dy[] = {1, 2, 2, 1, -1, -2, -2, -1};
int sx, sy;
for (int i = 0; i < n; i ++ )
for (int j = 0; j < m; j ++ )
if (g[i][j] == 'K')
sx = i, sy = j;
int hh = 0, tt = 0;
q[0] = {sx, sy};
memset(dist, -1, sizeof dist);//初始化
dist[sx][sy] = 0;
while (hh <= tt)
{
auto t = q[hh ++ ];
for (int i = 0; i < 8; i ++ )
{
int a = t.x + dx[i], b = t.y + dy[i];
if (a < 0 || a >= n || b < 0 || b >= m) continue;
if (g[a][b] == '*') continue;
if (dist[a][b] != -1) continue;
if (g[a][b] == 'H') return dist[t.x][t.y] + 1;
dist[a][b] = dist[t.x][t.y] + 1;
q[ ++ tt] = {a, b};
}
}
return -1;
}
int main()
{
cin >> m >> n;
for (int i = 0; i < n; i ++ ) cin >> g[i];
cout << bfs() << endl;
return 0;
}
作者:yxc
链接:https://www.acwing.com/activity/content/code/content/130867/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
抓住那头牛
思路
边权相同所以可以用BFS求最短路,但是要处理一下范围防止出现无限的情况,估计范围为0~2*K
代码
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int n,k;
int q[N];
int dist[N];//同时起到记录步数和标记是否访问过的作用
int bfs()
{
memset(dist,-1,sizeof dist);
dist[n]=0;
q[0]=n;
int hh=0,tt=0;
while(hh<=tt)
{
int t=q[hh++];
if(t==k)return dist[k];
if(t+1<N&&dist[t+1]==-1)
{
dist[t+1]=dist[t]+1;
q[++tt]=t+1;
}
if(t-1>=0&&dist[t-1]==-1)
{
dist[t-1]=dist[t]+1;
q[++tt]=t-1;
}
if(t*2<N&& dist[2*t]==-1)
{
dist[2*t]=dist[t]+1;
q[++tt]=2*t;
}
}
return -1;
}
int main()
{
scanf("%d%d",&n,&k);
printf("%d\n",bfs());
}