对于BFS的理解和部分例题(

(图文无关    雾

搜索是一个NOIP当中经常出现的考点,其实搜索换个方式来想也无非就是让电脑来帮你试,最后得到一个结果,当然这么口胡谁都会,那么我们就来看看搜索当中的一个大部分:

BFS(广度优先搜索)

首先我们看这样一道例题

P1451 求细胞数量

这个题吧,其实是我学习BFS遇到的第一道题,当然他作为一个很好的入门题让我很快的学会了BFS的基本模板

大概总结一下就是这样的

struct node{
    // 你要表示的状态,例如:坐标
}
node _node(/*参数*/){
    node r;
    //初始化
    return r;
}
/*
例如:
struct node{
    int x,y;
}
node _node(int x,int y){
    node r;
    r.x=x;
    r.y=y;
    return r;
}
*/
queue<node>q;
bool vis[...]...[...];//判重数组,保证每个状态只访问一次。
void bfs(node s/*起始元素*/){
    q.push(s);
    vis[...]...[...]=1;//初始状态纪录
    while(!q.empty()){
        node head=q.front();
        q.pop();
        for(...){//遍历所有能到达的状态
            node new=_node(head...)//新状态
            if(...new...&&!vis[...]...[...]){//新状态合法且没被访问过
                q.push(new);
                vis[...]...[...]=1;//新状态纪录。
            }
        }
    }
}

当然还是WZ大佬比较强%%%%%%%%%

然后吧我们来看一看这个题

其实是这样的

我们先写好上下左右四个方向的代码

int dx[4] = {-1, 0, 1, 0},
    dy[4] = {0, 1, 0, -1};

然后再计算的时候用一个for循环实现就行了具体的思路是这样的

找到一个点的x,y---->对于这个点上下左右(四联通)进行判断,如果是细胞就把其坐标压进队列,如果不是就跳过,当我们处理完一个细胞(假设他周围有细胞的话),就会继续处理它周围的细胞,知道把所有细胞的四联通都找完为止,对了我们把一个细胞找完之后要把它清为false,不然会出错

上代码

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
bool b[101][101];
int n, m, a[101][101], ans = 0;
int dx[4] = {-1, 0, 1, 0},
    dy[4] = {0, 1, 0, -1};
inline void qaq(int x, int y)
{
    int hx[1000], hy[1000], head = 1, tail = 1, tx, ty;
    ans++;
    hx[1] = x, hy[1] = y;
    b[x][y] = false;
    for (; head <= tail; ++head)
    {
        for (int i = 0; i <= 3; ++i)
        {
            tx = hx[head] + dx[i],
            ty = hy[head] + dy[i];
            if (tx > 0 && tx <= m && ty > 0 && ty <= n && b[tx][ty])
            {
                tail++;
                hx[tail] = tx,
                hy[tail] = ty;
                b[tx][ty] = false;
            }
        }
    }
}
int main()
{
    scanf("%d%d", &m, &n);
    for (int i = 1; i <= m; ++i)
        for (int j = 1; j <= n; ++j)
            b[i][j] = true;
    for (int i = 1; i <= m; ++i)
        for (int j = 1; j <= n; ++j)
        {
            scanf("%1d", &a[i][j]);
            if (!a[i][j])
                b[i][j] = false;
        }
    for (int i = 1; i <= m; ++i)
        for (int j = 1; j <= n; ++j)
            if (b[i][j])
                qaq(i, j);
    printf("%d", ans);
    return 0;
}

T2

P1162 填涂颜色

这个题其实和上一道差不多,也是找连通块然后进行处理,那么做这个题的时候我们需要考虑些什么呢?

首先,因为涂色区域一定是被包围着的,所以他一定是2~~~~n-1这个范围内的,循环的时候就可以这么写(不过不影响大局)

然后他说只把中间那个区域涂上颜色,换个思路就是我们把所有边上的连通块全都处理掉不就好了嘛~

上代码

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
int n, a[31][31], dx[4] = {1, 0, -1, 0}, dy[4] = {0, 1, 0, -1}, X, Y;
bool b[31][31];
void qaq(int x, int y)
{
    int q[900][2], head = 1, tail = 0;
    tail++;
    q[tail][1] = x;
    q[tail][2] = y;
    b[x][y] = 1;
    for (; head <= tail; ++head)
    {
        for (int i = 0; i <= 3; ++i)
        {
            X = q[head][1] + dx[i];
            Y = q[head][2] + dy[i];
            if (X > 0 && X <= n && Y > 0 && Y <= n && b[X][Y] == 0)
            {
                tail++;
                q[tail][1] = X;
                q[tail][2] = Y;
                b[X][Y] = 1;
            }
        }
    }
}
int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; ++i)
        for (int j = 1; j <= n; ++j)
        {
            scanf("%d", &a[i][j]);
            if (a[i][j])
                b[i][j] = 1;
        }
    //printf("\n\n\n\n\n\n");
    for (int i = 1; i <= n; ++i)
        for (int j = 1; j <= n; ++j)
            if (!a[i][j] && (i == 1 || j == 1 || i == n || j == n))//这里就是保证连通块必然有至少一个数字在边上
                qaq(i, j);
    for (int i = 1; i <= n; ++i)
    {
        for (int j = 1; j <= n; ++j)
        {
            if (!b[i][j])
                printf("2 ");
            else
                printf("%d ", a[i][j]);
        }
        printf("\n");
    }
}

 

现在我们来看BFS的另外一个大类型

迷宫问题~~~~~

说真的,做到这东西的时候,我的内心是这样的

 

 

因为迷宫我一开始不会,就先做了神奇题目_GC滑迷宫(毕竟是自己参与题目构思的嘛~)

题目有限制同学们就自己去团队里头找就好啦

首先这个题的思路是把地图读入,然后从起始坐标开始搜

因为_GC只能滑着走啊,所以这货就直接让他撞墙(a[x][y]==1)的时候换个方向继续划水就行了

有两个操作比较好

判出界操作和判初始位置这个操作(虽然正常人测试点不会这么诡异)

上代码(虽然黈力,WZ别打我)

#include <iostream>
#include <cstdio>
#include <queue>
#include <algorithm>
using namespace std;
int n, m, sx, sy;
bool mat[501][501];
const int dx[4] = {0, 1, 0, -1}, dy[4] = {1, 0, -1, 0};
struct pos
{
    int x, y, step;
    pos(int x, int y, int step) : x(x), y(y), step(step) {}
};
queue<pos> q;
bool vis[501][501];
inline bool pan(int x, int y)//判断合法性
{
    return x >= 1 && x <= n && y >= 1 && y <= m && mat[x][y] == 0;
}
bool out(int x, int y)//判断是否已到达迷宫边界,即可以离开迷宫
{
    return (x == 1 || x == n || y == 1 || y == m) && mat[x][y] == 0;
}
int main()
{
    cin >> n >> m;
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= m; j++)
        {
            cin >> mat[i][j];
        }
    }
    cin >> sx >> sy;
    if(mat[sx][sy]){//如果初始位置是墙,则无解
        cout<<-1;
        return 0;
    }
    if(out(sx,sy)){//如果初始位置在边界,则直接离开
        cout<<0;
        return 0;
    }
    q.push(pos(sx, sy, 0));
    vis[sx][sy] = 1;
    while (!q.empty())
    {
        pos h = q.front();
        q.pop();
        // cout << h.x << ' ' << h.y << endl;
        if (out(h.x, h.y))//如果到达出口
        {
            cout << h.step - 1;//因为原题求的是撞墙次数,而我们知道最后一次是不撞墙的
            return 0;//直接退出,因为已求得最优解
        }
        for (int i = 0; i < 4; i++)
        {
            int xx = h.x, yy = h.y;
            while (pan(xx + dx[i], yy + dy[i]))//如果没撞到墙,就继续走
            {
                xx += dx[i];
                yy += dy[i];
            }
            if ((xx == h.x && yy == h.y) || vis[xx][yy])//如果并没有移动,或者最终位置已到达过
                continue;
            vis[xx][yy] = 1;
            q.push(pos(xx, yy, h.step + 1));
        }
    }
    cout << -1;//如果所有可能节点都已遍历,而始终没有到达边界,则无解
}

 

在慢慢的看完了_GC滑迷宫之后,我开始试着做迷宫了,

P1605 迷宫

 迷宫的操作其实就是把滑的部分简化,这里直接用到连通块那个dx和dy就行

对于每一个点都进行四联通操作,然后递归的来模拟走的路线,因为是递归求解,每一个方向只走一次,所以路线就不会重复,如果到了终点,我们就把路线+1

代码如下

#include <iostream>
#include <cstdio>
using namespace std;
int n, m, t, ex, ey, ans = 0, bx, by, tx, ty, X, Y;
int dx[4] = {0, 0, 1, -1};
int dy[4] = {-1, 1, 0, 0};
bool pos[6][6], map[6][6];
void qaq(int x, int y)
{
    if (x == ex && y == ey)
    {
        ans++;
        return;
    }
    else
    {
        for (int i = 0; i <= 3; ++i)
        {
            X = x + dx[i];
            Y = y + dy[i];
            if (!pos[X][Y] && map[X][Y] && X<=m && x>0 && y<=n && Y>0) //没被走过而且不是障碍
            {
                pos[x][y] = 1;
                qaq(X, Y);
                pos[x][y] = 0;
            }
        }
    }
}
int main()
{
    scanf("%d%d%d", &n, &m, &t);
    for (int i = 1; i <= n; ++i)
        for (int j = 1; j <= m; ++j)
            map[i][j] = true;
    scanf("%d%d%d%d", &bx, &by, &ex, &ey);
    for (int i = 1; i <= t; ++i)
    {
        scanf("%d%d", &tx, &ty);
        map[tx][ty] = false; //障碍
    }
    qaq(bx, by);
    printf("%d", ans);
    return 0;
}
/*
5 5 0
1 1 2 2


4846
*/

这里吧,有一个很坑的点,我因为一开始写的太快了,就忘了加上判边界的情况,所以就只得了80,这也算是一个教训,因为小样例一般比较简单也没什么坑,但是以后写题的时候还是加上更多的判断比较好

时间不够了先咕咕咕咕咕,明天再写

猜你喜欢

转载自www.cnblogs.com/this-is-M/p/10704427.html
0条评论
添加一条新回复