[leetcode934]——最短的桥(深度搜索+广度搜索)

在给定的二维二进制数组 A 中,存在两座岛。(岛是由四面相连的 1 形成的一个最大组。)

现在,我们可以将 0 变为 1,以使两座岛连接起来,变成一座岛。

返回必须翻转的 0 的最小数目。(可以保证答案至少是 1。)

 

示例 1:

输入:[[0,1],[1,0]]
输出:1

示例 2:

输入:[[0,1,0],[0,0,0],[0,0,1]]
输出:2

示例 3:

输入:[[1,1,1,1,1],[1,0,0,0,1],[1,0,1,0,1],[1,0,0,0,1],[1,1,1,1,1]]
输出:1

其实题目本身不难,这道题我第一反应就是用深搜+广搜的结合,起初以为会超时,没想到过了,不过这个做法在我机子上耗时320ms,有点低效,暂时也没想到别的方法,就把这个做法贴出来先吧。

思路:题目指定了只有两个岛,并且一开始是不相连的,问我们至少要把多少个0变成1才能相连。其实这就是一个类似迷宫的场景,即给定两个点,一个为起点,另一个为终点,问起点到终点最少需要的步数,这不过没有障碍物或者墙壁而已。转换到这道题就变成求一个岛到另一个岛最短的距离。为了区分这两个岛,我们可以先把其中一个岛的数字1全部变成数字2,另外一个岛不变。题意就转换成了全部数字为1组成的岛到全部数字为2组成的岛的最短距离了。

拿实例三作为演示:

一开始岛是这个样子

1,1,1,1,1

1,0,0,0,1

1,0,1,0,1

1,0,0,0,1

1,1,1,1,1

现在我把第一个岛全部的数字都变成2

2,2,2,2,2

2,0,0,0,2

2,0,1,0,2

2,0,0,0,2

2,2,2,2,2

那么现在就是求中间那个数字为1的岛到由数字2组成的岛的最短距离了。

所以总结下来也就两步:一、把其中一个岛的全部数字变成2(这里利用深搜)。二、求数字1的岛到数字2的岛的距离(也可以求数字2岛到数字1岛的距离,这里利用广搜)。

直接上代码,具体做法看注释

class Solution {
public:
    int shortestBridge(vector<vector<int>>& A) {
        //老规则,一上来先判断二维数组是否为空
        if(A.empty()||A[0].empty())
            return 0;
        int m = A.size(),n = A[0].size(); //m代表行数,n代表列数
        int i,j;
        vector<vector<int>> move = {{1,0},{0,1},{-1,0},{0,-1}}; //四个方向,在广搜的时候用到
        bool flag = false;
        //执行第一步,把其中一个岛的数字全变成2
        for(i = 0;i<m;i++)
        {
            for(j = 0;j<n;j++)
            {
                if(A[i][j] == 1)  //遇到岛了
                {
                    flag = true;
                    dfs(A,i,j,m,n);   //利用深搜把第一个岛的1全部变成2
                    break;          //把一个岛数字全都变成2之后直接跳出循环,因为不需要把另外一个岛变成2了
                }
            }
            if(flag) break;
        }
        
        //一开始给答案设一个无限大的值
        int res = 0x3f3f3f3f; 
        for(i = 0;i<m;i++)
        {
            for(j = 0;j<n;j++)
            {
                if(A[i][j] == 1)    //找到另外一个岛,我们需要对这个岛的每一个点都做广搜(寻找到数字2岛的距离),这些点中与另外一个岛的最短的那个距离即为答案
                {
                    //state数组用来做步数记录,我们记一个点为一步
                    vector<vector<int>> state(m,vector<int>(n,0));
                    //起初我们给出发的点设置步数为一(记住,这个很关键)
                    state[i][j] = 1; 
                    res = min(res,bfs(A,move,state,i,j));
                }
            }
        }
        return res;   
    }
    
    bool bound(int i,int j,int m,int n)   //越界情况返回true,再广搜的时候用到
    {
        if(i<0||j<0||i>=m||j>=n)
            return true;
        return false;
    }
    
    //把岛中的数字1全部变成2
    void dfs(vector<vector<int>>& A,int i,int j,int m,int n)
    {
        if(i<0||j<0||i>=m||j>=n)   //越界退出
            return;
        if(A[i][j] == 1)          //凡是遇到1就变成2
        {
            A[i][j] = 2;
            //深度扩散
            dfs(A,i+1,j,m,n);   
            dfs(A,i-1,j,m,n);
            dfs(A,i,j+1,m,n);
            dfs(A,i,j-1,m,n);
        }
    }
    
    //求点A(i,j)到值为2的坐标点的最短距离
    int bfs(vector<vector<int>>& A,vector<vector<int>>& move,vector<vector<int>>& state,int i,int j)
    {
        int m = A.size(),n = A[0].size();
        queue<pair<int,int>> q;  //创建一个队列记录我们即将走的坐标点
        q.push({i,j});           //一开始我们是在(i,j)这个坐标点出发的,记录进队列中
        while(!q.empty())
        {
            pair<int,int> p = q.front();q.pop();
            if(A[p.first][p.second] == 2)    //当来到了数字全为2的岛上时,可以返回步数-2,-2是因为我们走多了两步,一步是起始的位置,另一步是终点的位置。实际上这两步是不需要计算进去的
                return state[p.first][p.second]-2;
            //利用move数组,以当前位置为中心向四周走
            for(int i = 0;i<4;i++)   
            {
                //(px,py)为下一个要走的坐标点
                int px = p.first+move[i][0],py = p.second+move[i][1];
                //当越界了就跳到下一个循环
                if(bound(px,py,m,n))
                    continue;
                //当我们之前曾经来过当前点或者当前点为自己原本的岛的时候,跳到下一个循环
                if(state[px][py]||A[px][py] == 1)
                    continue;
                //把下次要走的坐标记录进队列中
                state[px][py] = state[p.first][p.second]+1;
                q.push({px,py});
            }
        }
        return 0x3f3f3f3f;
    }
};

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值