(多源广度优先搜索)leetcode-994. 腐烂的橘子

题目链接:力扣

首先给出单源广度优先搜索的基本思路,参考广度优先搜索(BFS)_PL_涵的博客-CSDN博客

练习题:AcWing 845. 八数码 - AcWing

#include<iostream>
#include<queue>
#include<unordered_map>
using namespace std;

const int N=3;
unordered_map<string,int> d; //距离数组,存储每个状态到起始状态的距离(即最初的矩阵要经过几次变化达到现在的状态)
queue<string> q; //状态队列,用于存储每一个要进行bfs的状态

//对每个state做bfs
int bfs(string state){
    const string end="12345678x"; //正确排列,即结束状态
    int dx[4]={1,0,-1,0},dy[4]={0,1,0,-1};
    while(!q.empty()){ //还有状态能被bfs时
        //取队头状态,开始bfs
        auto t=q.front();
        q.pop();
        int distance=d[t];
        if(t==end) return distance; //若已找到正确排列(即当前队头就是),就返回
        int pos=t.find('x');
        int x=pos/N,y=pos%N; //把x在字符串中的位置转成在N*N矩阵中的位置
        for(int i=0;i<4;i++){//当前队头不是正确排列,还没找到,就bfs当前队头的状态,看看有没有新状态可以添加进队列(后续会对这些新状态进行同样的bfs过程)
            int a=x+dx[i],b=y+dy[i];
            if(a>=0 && a<N && b>=0 && b<N){
                swap(t[pos],t[a*N+b]); //新状态:把当前状态中的x和其前/后/左/右元素互换(如果这些元素存在,未越界)
                 //count()返回指定参数key的键的元素数,由于unordered_map不允许重复,因此返回1/0
                if(!d.count(t)){//如果上面得到的新状态没在unordered_map中出现过,说明是第一次出现,则加入队列
                    q.push(t);
                    d[t]=distance+1;
                }
                swap(t[pos],t[a*N+b]); //bfs完一种状态(如在最初队头状态t的基础上,换了x和其左边的一个元素),恢复现场,开始下一个状态的bfs(在最初队头状态t的基础上,换了x和其上/下/右边的一个元素)
            }
        }
    }
    return -1; //循环完了都没发现正确排列,就返回-1
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);

    string start;
    for(int i=0;i<N;i++){
        for(int j=0;j<N;j++){
            char c;
            cin>>c;
            start+=c;
        }
    }
    //初始化距离数组和状态队列
    d[start]=0;
    q.push(start);
    cout<<bfs(start)<<endl;

    return 0;
}

而多源广度优先搜索相比于单源,无非就是最初初始化队列时,单源只需要加入一个源起点,而多源则需要把所有的多个起点都加入队列中,后面的步骤就差不多了。上代码:

class Solution {
public:
    int orangesRotting(vector<vector<int>>& grid) {
        //m*n网格(矩阵)
        int m=grid.size();
        int n=grid[0].size();

        queue<pair<int,int>> q;
        int cnt=0; //新鲜橘子的数量
        for(int i=0;i<m;i++){
            for(int j=0;j<n;j++){
                if(grid[i][j]==2) q.push({i,j}); //腐烂的橘子入队
                else if(grid[i][j]==1) cnt++;
            }
        }

        //多源广度优先搜索
        int time=0; //橘子全腐烂需要的时间
        int dx[4]={1,0,-1,0},dy[4]={0,1,0,-1};
        while(!q.empty()){
            auto t=q.front();
            q.pop();
            for(int i=0;i<4;i++){
                int x=t.first+dx[i];
                int y=t.second+dy[i];
                if(x>=0 && x<m && y>=0 && y<n && grid[x][y]==1){
                    cnt--;
                    q.push({x,y});
                    grid[x][y]=grid[t.first][t.second]+1; //记录每个网格中橘子变腐烂时的时间(=经过的时间+2, 2是腐烂橘子源单元格内的时间). 这里时间一定>=2, 和1/0不冲突, 因此后面不会产生把更新过的橘子认为是没腐烂过的橘子/空网格的误区。因此可以用来判断该点是否被访问过
                    time=grid[x][y]-2; //上面+2, 这边要-2才能还原出经过的时间
                }
            }
        }

        if(!cnt) return time;
        return -1;
    }
};

本题的代码有一个很巧妙的点:在判断一个点是否被访问过时,不需要单独开一个数组来记录访问状态,而是直接在原数组上修改值就可以

grid[x][y]=grid[t.first][t.second]+1; //记录每个网格中橘子变腐烂时的时间(=经过的时间+2, 2是腐烂橘子源单元格内的时间). 这里时间一定>=2, 和1/0不冲突, 因此后面不会产生把更新过的橘子认为是没腐烂过的橘子/空网格的误区。因此可以用来判断该点是否被访问过

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值