剑指offer:JZ66 机器人的运动范围

机器人从[0,0]开始在rows行cols列的网格中移动,行列数位之和不超过threshold。本文介绍了使用深度优先搜索(DFS)和广度优先搜索(BFS)算法来确定机器人能到达的格子数量。对于DFS,提供了粗暴版和简化版的实现;BFS则利用队列按距离扩展搜索范围。" 101950804,8291491,C++ 面向对象实战:类操作与友元深入解析,"['C++编程', '面向对象设计', '类与对象', '友元函数', '类型转换', '函数重载']

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

描述

地上有一个rows行和cols列的方格。坐标从 [0,0] 到 [rows-1,cols-1]。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于threshold的格子。 例如,当threshold为18时,机器人能够进入方格[35,37],因为3+5+3+7 = 18。但是,它不能进入方格[35,38],因为3+5+3+8 = 19。请问该机器人能够达到多少个格子?

范围:

1 <= rows, cols<= 100;0 <= threshold <= 20

示例

input1:1,2,3
output1:3

input2:0,1,3
output2:1

input3:10,1,100
output3:29
说明3[0,0],[0,1],[0,2],[0,3],[0,4],[0,5],[0,6],[0,7],[0,8],[0,9],[0,10],[0,11],[0,12],[0,13],[0,14],
[0,15],[0,16],[0,17],[0,18],[0,19],[0,20],[0,21],[0,22],[0,23],[0,24],[0,25],[0,26],[0,27],
[0,28]29种,后面的[0,29],[0,30]以及[0,31]等等是无法到达的   

input4:5,10,10
output4:21

思路

从题目中可以看出,由于机器人每次只能移动1格,并且行列坐标的数位之和有限制,因此必然需要对能够访问的格子进行遍历,很容易想到两种方法:深度优先和广度优先。
时间复杂度为:O(M*N),考虑极端情况每个格子都访问一遍
空间复杂度为:O(M*N),需要创建二维数组

1、深度优先(DFS)

DFS算法的思路是往一个方向一直走到底,不满足条件时才会回头,继续走到底,等所有能够访问的格子都访问了遍历就结束了。在题目中,机器人可以在四个方向上移动,因此DFS算法的主体是往四个方向走,并且当前的格子需要标记为已访问,出口条件则是分为几种:
(1)行、列的索引超出范围;行列坐标的数位之和超出阈值
(2)当前格子已访问

——粗暴版

在该版本中,直接把计数的变量设为全局,并且按照四个方向进行移动。这种思路法简单粗暴,对DFS的问题具有通用性

public class Solution {
    public int count = 0;//全局变量计数
    public int movingCount(int threshold, int rows, int cols) {
        boolean[][] arr = new boolean[rows][cols];
        dfs(arr, 0, 0, threshold);
        return count;
    }
    
    //计算数字的数位之和
    public int getBitCnt(int num){
        int cnt = 0;
        while(num != 0){
            cnt += num % 10;
            num /= 10;
        }
        return cnt;
    }
    
    //DFS算法的实现
    public void dfs(boolean[][] arr, int row, int col, int th){
        boolean flag = row < 0 || row == arr.length;
        flag = flag || col < 0 || col == arr[0].length;
        flag = flag || getBitCnt(row) + getBitCnt(col) > th;
        flag = flag || arr[row][col] == true;
        if(flag) return;
        arr[row][col] = true;
        count++;
        dfs(arr, row+1, col, th);
        dfs(arr, row-1, col, th);
        dfs(arr, row, col+1, th);
        dfs(arr, row, col-1, th);
    }
}

粗暴版DFS

——简化版

通过题目进行分析,由于起点在左上角(0, 0),因此下一格必然是右或者下,因此可以不考虑左和上;同时,为了节省空间,可以直接让DFS返回计数值,由3部分组成:
(1)当前已经满足条件的格子,计数为1
(2)当前格子右方的格子:dfs(arr, row, col+1, th)
(3)当前格子下方的格子:dfs(arr, row+1, col, th)

    public int dfs(boolean[][] arr, int row, int col, int th){
        boolean flag = row == arr.length || col == arr[0].length;
        flag = flag || getBitCnt(row) + getBitCnt(col) > th;
        flag = flag || arr[row][col] == true;
        if(flag) return 0;
        arr[row][col] = true;
        return 1 + dfs(arr, row+1, col, th) + dfs(arr, row, col+1, th);
    }

简化版DFS

2、广度优先(BFS)

BFS算法的思路是按照距离进行访问,在本题中先访问离自己最近的一圈,接着往外扩张,直到整个格子访问完成。在该算法中,第一圈就是左上角(0, 0),第二圈则是右和下,第三圈则是第二圈所有元素的右和下,依此类推。

在BFS算法中,一般实现的方法是使用队列,这样可以保证最近的一圈访问后,这一圈的元素被标记为以访问并且下一圈的所有元素也被加入到队列中

import java.util.Queue;
import java.util.LinkedList;
public class Solution {
    public int movingCount(int threshold, int rows, int cols) {
        boolean[][] flag = new boolean[rows][cols];//用于标记格子是否被访问
        int cnt = 0;//用以计数
        Queue<int[]> que = new LinkedList<>();//队列只存放某一圈的元素
        que.add(new int[]{0, 0});//第一圈从(0, 0)开始,只有一个元素
        while(!que.isEmpty()){
            int[] e = que.poll();
            int row = e[0], col = e[1];
            if(row == rows || col == cols || getBitCnt(row)+getBitCnt(col) > threshold || flag[row][col] == true)
                continue;
            cnt++;
            flag[row][col] = true;
            //第一圈弹出后,则加入第二圈:即其右和下方的元素
            que.add(new int[]{row+1, col});
            que.add(new int[]{row, col+1});
        }
        return cnt;
    }
    
    public int getBitCnt(int num){
        int cnt = 0;
        while(num != 0){
            cnt += num % 10;
            num /= 10;
        }
        return cnt;
    }
}

BFS

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值