(bfs&dfs)洛谷P1451 求细胞数量

(bfs&dfs)洛谷P1451 求细胞数量

题目传送门:P1451 求细胞数量

题目描述

一矩形阵列由数字 0 到 9 组成,数字 1 到 9代表细胞,细胞的定义为沿细胞数字上下左右若还是细胞数字则为同一细胞,求给定矩形阵列的细胞个数。

输入格式

第一行两个整数代表矩阵大小 n 和 m。

接下来 n 行,每行一个长度为 m 的只含字符 0 到 9 的字符串,代表这个 n×m 的矩阵。

输出格式

一行一个整数代表细胞个数。

输入输出样例

输入 #1复制

4 10
0234500067
1034560500
2045600671
0000000089

输出 #1复制

4

说明/提示

数据规模与约定
对于 100% 的数据,保证 1≤n,m≤100。

分析:

这道题是一道非常简单的模板题,我做这道题是想更清晰的区别dfs和bfs。
从题意中可知细胞的定义为沿细胞数字上下左右若还是细胞数字则为同一细胞,即不论是上或下或左或右,只要不为0,则这一块是同一个细胞。简单的来说就是求连通块。
所以题目所给样例中有4个连通块。
在这里插入图片描述
这道题的输入有点意思,当我们把数组定义为int型时,我们不能直接输入每一个数,这时可以采用scanf("%1d",&arr[i][j]);来控制输入元素的个数。还有一种方法是我们把数组定义为char型,然后再输入。
接下来,先用上篇总结过的dfs来写这道题。
首先输入n和m,然后将每个元素输入。
然后我们需要判断边界条件以及合法状态(即是否被标记过)
接下来我们需要从四个方向(上下左右)搜索,看搜索到的元素是否满足边界条件或者处于合法状态。
如果不满足,则换下一个方向,否则标记一下。
每当一个连通块被搜索结束后,我们就记录一下块的个数,然后搜索下一个块。

dfs代码:

#include<bits/stdc++.h>
using namespace std;
int g[105][105];
int n,m,sum=0;
bool used[105][105];//标记数组
int dx[]={0,0,1,-1};//方向数组
int dy[]={1,-1,0,0};
//判断边界或是否被标记过
bool check(int a,int b)
{
    if(a<1||a>n||b<1||b>m||g[a][b]==0||used[a][b]==false)
        return false;
    else 
        return true;
}
//深搜
void dfs(int a,int b)
{
    used[a][b]=false;
    for(int i=0;i<4;i++)
    {
        int tx,ty;
        tx=a+dx[i];ty=b+dy[i];
        if(!check(tx,ty))
            continue;  
        dfs(tx,ty);
        used[tx][ty]=false;
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        {
            scanf("%1d",&g[i][j]);//有两种输入方法,当用char定义g[i][j]时,用cin>>g[i][j];用int定义g[i][j]时,用scanf("%1d",&g[i][j]);来控制输入的个数
            used[i][j]=true;
        }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        {
            if(g[i][j]!=0&&used[i][j])
            {
                dfs(i,j);
                sum++;
            }
        }
        cout<<sum<<endl;
    return 0;
}

接下来,我们采用bfs来做这道题。
首先,总结一下bfs算法。广度优先搜索(也称宽度优先搜索,缩写BFS,以下采用广度来描述)是连通图的一种遍历策略。因为它的思想是从一个顶点V0开始,辐射状地优先遍历其周围较广的区域。一般用来求最短路径, 迷宫问题。
它的遍历过程如下:首先访问指定的初始节点,然后若当前访问的节点的邻接节点有未被访问的,则依次访问每一个邻接节点;否则,返回到访问过的第一个邻接节点;然后看该节点是否有未被访问过的邻接节点,然后依次访问。如果没有邻接节点了,则返回到最开始访问的第二个邻接节点(初始节点访问的第二个),然后继续访问直到访问结束。然后再第三个,…,然后重复上述过程;最后遍历结束。

下面,我们用上篇的图来模拟一下:
在这里插入图片描述
首先我们随机选择一个节点作为起始点(以V1作为起始点为例)

以V1作为起始点(标记),与V1相邻的节点有V2,V3,依次访问V2,V3,访问V2(标记),访问v3(标记),然后V2有两个节点 V4,V5,依次访问V4(标记),v5(标记),V3有两个节点V6,V7,依次访问V6(标记),V7(标记);V4有一个节点 V8,访问V8(标记),遍历结束。

访问顺序:V1->V2->V3->V4->V5->V6->V7->V8

附上bfs模板:

int dir[4][2]= {0,1,0,-1,1,0,-1,0}; // 方向向量
struct State // BFS 队列中的状态数据结构
{
    int x,y; // 坐标位置
    int Step_Counter; // 搜索步数统计器
};
State a[maxn];

bool CheckState(State s) // 约束条件检验
{
    if(!used[s.x][s.y] && ...) // 边界或未被标记
        return true;
    else // 约束条件冲突
        return false;
}

void bfs(State st)
{
    queue <State> q; // BFS 队列
    State now,next; // 定义2 个状态,当前和下一个
	q.push(st); // 入队
    used[st.x][st.y]=false; // 访问标记
    while(!q.empty())//当队列不为空时
    {
        now=q.front(); // 取队首元素进行扩展
        if(now==G) //
        {
            ...... // 做相关处理
            return;
        }
        for(int i=0; i<4; i++)
        {
            next.x=now.x+dir[i][0]; // 按照规则生成下一个状态
            next.y=now.y+dir[i][1];
            if(CheckState(next)) // 如果状态满足约束条件则入队
            {
                q.push(next);//入队
                used[next.x][next.y]=false; //访问标记
            }
        }
        q.pop(); // 队首元素出队
    }
}

接下来,我们套用模板来写这道题。
大部分步骤和dfs相同,唯一不同的地方是bfs用的是队列,并且不用回溯。注意哦,队列是先进先出。

bfs代码:

#include<bits/stdc++.h>
using namespace std;
int g[105][105];
struct node{
    int x,y;
};
int n,m,sum=0;
bool used[105][105];//标记数组
int dx[]={0,0,1,-1};//方向数组
int dy[]={1,-1,0,0};
//判断边界或是否被标记过
bool check(int a,int b)
{
    if(a<1||a>n||b<1||b>m||g[a][b]==0||used[a][b]==false)
        return false;
    else 
        return true;
}
//广搜
void bfs(int a,int b)
{
    queue<node> q;
    node now,next;
    used[a][b]=false;
    now.x=a;now.y=b;
    q.push(now);//入队
    while(!q.empty())
    {
        now=q.front();//返回队首元素
        for(int i=0;i<4;i++)
        {
            int tx=now.x+dx[i];
            int ty=now.y+dy[i];
            if(!check(tx,ty))
                continue;
            next.x=tx;next.y=ty;
            used[tx][ty]=false;
            q.push(next);
        }
        q.pop();//首元素出队
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        {
            scanf("%1d",&g[i][j]);//有两种输入方法,当用char定义g[i][j]时,用cin>>g[i][j];用int定义g[i][j]时,用scanf("%1d",&g[i][j]);来控制输入的个数
            used[i][j]=true;
        }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        {
            if(g[i][j]!=0&&used[i][j])
            {
                bfs(i,j);
                sum++;
            }
        }
        cout<<sum<<endl;
    return 0;
}
  • 10
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
### 回答1: 题目描述:给定一个由数字到9组成矩形阵列,其中数字1到9代表细胞细胞定义为沿细胞数字上下左右还是细胞数字则为同一细胞给定矩形阵列细胞个数。 解题思路:可以使用深度优先搜索(DFS)或广度优先搜索(BFS)来解决此问题。对于每个细胞,我们可以从它开始,向上下左右四个方向搜索,如果搜索到的数字是1到9,则说明是同一细胞,继续搜索下去。搜索过的细胞可以标记为已访问,避免重复计算。 代码实现:可以使用递归实现DFS,也可以使用队列实现BFS。以下是DFS的代码实现: ### 回答2: 题目描述:给定一个由数字0到9组成矩形阵列,其中数字1到9代表细胞细胞定义为沿细胞数字上下左右还是细胞数字则为同一细胞。请计算给定矩形阵列细胞的个数。 思路分析:此题需要通过遍历矩阵,判断每个数字是否为细胞来计算细胞的个数。具体来说,可以通过深度优先搜索或广度优先搜索实现。以深度优先搜索为例,对于每个数字为1-9的位置,从当前位置出发,搜索其上下左右四个方向,如果该方向上的数字也为1--9,那么继续搜索,否则返回。在搜索时,可以通过将已访问过的数字标记为‘0’,避免重复访问。最终,矩阵中标记为‘1’的数字个数就是细胞的个数。 代码实现: //定义方向数组 int dx[4]={1,-1,0,0}; int dy[4]={0,0,1,-1}; //DFS搜索 void dfs(vector<vector<char>>& grid,int x,int y){ int n=grid.size(); int m=grid[0].size(); //搜索完当前细胞后,将其标记为‘0’,避免重复访问 grid[x][y]='0'; for(int i=0;i<4;i++){ int nx=x+dx[i]; int ny=y+dy[i]; if(nx>=0 && nx<n && ny>=0 && ny<m && grid[nx][ny]>'0'){ dfs(grid,nx,ny); } } } int countCells(vector<vector<char>>& grid) { int n=grid.size(); int m=grid[0].size(); int res=0; for(int i=0;i<n;i++){ for(int j=0;j<m;j++){ if(grid[i][j]>'0'){ dfs(grid,i,j); res++; } } } return res; } 时间复杂度:O(nm) 空间复杂度:O(nm) ### 回答3: 这是一个常见的二维数组/矩阵问题,可以用深度优先搜索(DFS)或广度优先搜索(BFS)来解决。 以DFS为例,我们可以先遍历整个矩阵,遇到第一个数字为1的格子就进入DFS,依次搜索4个方向,将访问过的细胞标记为'#',直到找完与该细胞相邻的所有细胞,然后回溯到上一级,继续搜索下一个未被标记的细胞,直到整个矩阵都被遍历完为止,记录被访问的细胞个数即可。 具体实现时,可以用一个visited数组记录每个点是否已经被访问过,若访问到的点为'#'或越界或数字为0,则返回,否则继续搜索该点的四周。 以下是DFS的代码实现(C++): ``` int dfs(vector<vector<char>>& grid, int i, int j) { if (i < 0 || j < 0 || i >= grid.size() || j >= grid[0].size() || grid[i][j] == '#' || grid[i][j] == '0') return 0; grid[i][j] = '#'; return 1 + dfs(grid, i - 1, j) + dfs(grid, i + 1, j) + dfs(grid, i, j - 1) + dfs(grid, i, j + 1); } int countCells(vector<vector<char>>& grid) { int count = 0; for (int i = 0; i < grid.size(); i++) { for (int j = 0; j < grid[0].size(); j++) { if (grid[i][j] == '1') count += dfs(grid, i, j); } } return count; } ``` 其中,grid是二维矩阵,'0'~'9'是该矩阵内的数字,'#'表示该点已经被访问过,count表示细胞个数。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值