剑指 Offer 13. 机器人的运动范围(中等)【BFS、DFS、DP】

题目描述

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

示例 1:
输入:m = 2, n = 3, k = 1
输出:3
示例 2:
输入:m = 3, n = 1, k = 0
输出:1

提示:
1 <= n,m <= 100
0 <= k <= 20

题解

如何计算一个数的数位之和呢?我们只需要对数 x 每次对 10 取余,就能知道数 x 的个位数是多少,然后再将 x 除 10,这个操作等价于将 x 的十进制数向右移一位,删除个位数(类似于二进制中的 >> 右移运算符),不断重复直到 x 为 0 时结束。

题解1:广度优先搜索:BFS

class Solution {
    // 计算 x 的数位之和
    int get(int x) {
        int res=0;
        for (; x; x /= 10) {//x > 0,就需要求余,不断求余求个位、十位、百位...的值。
            res += x % 10;
        }
        return res;
    }
public:
    int movingCount(int m, int n, int k) {
        if (!k) return 1;
        queue<pair<int,int> > Q;//https://blog.youkuaiyun.com/Enterprise_/article/details/73695255
        // 向右和向下的方向数组
        int dx[2] = {0, 1};
        int dy[2] = {1, 0};
        vector<vector<int> > vis(m, vector<int>(n, 0));
        Q.push(make_pair(0, 0));
        vis[0][0] = 1;
        int ans = 1;//vis[x][y] == 1表示该位置满足条件,机器人可以跳到
        while (!Q.empty()) {
            auto [x, y] = Q.front();//队列先进先出,尾进头出
            Q.pop();
            for (int i = 0; i < 2; ++i) {
                int tx = x + dx[i];//当前位置(x,y)像右或者向下移动得到新位置(tx,ty)
                int ty = y + dy[i];
                if (tx < 0 || tx >= m || ty < 0 || ty >= n || vis[tx][ty] || get(tx) + get(ty) > k) continue;//判断新位置(tx,ty)是否满足条件
                Q.push(make_pair(tx, ty));//满足条件将其加入队列
                vis[tx][ty] = 1;
                ans++;
            }
        }
        return ans;
    }
};

复杂度分析:

  • 时间复杂度:O(mn),其中 m 为方格的行数,n 为方格的列数。考虑所有格子都能进入,那么搜索的时候一个格子最多会被访问的次数为常数,所以时间复杂度为 O(2mn)=O(mn)。
  • 空间复杂度:O(mn),其中 m 为方格的行数,n 为方格的列数。搜索的时候需要一个大小为 O(mn) 的标记结构用来标记每个格子是否被走过。

题解2:递推

思路:
考虑到方法一提到搜索的方向只需要朝下或朝右,我们可以得出一种递推的求解方法。

算法:

定义 vis[i][j] 为 (i, j) 坐标是否可达,如果可达返回 1,否则返回 0。【有动态规划的感觉了】

首先 (i, j) 本身需要可以进入,因此需要先判断 i 和 j 的数位之和是否大于 k ,如果大于的话直接设置 vis[i][j] 为不可达即可。

否则,前面提到搜索方向只需朝下或朝右,因此 (i, j) 的格子只会从 (i - 1, j) 或者 (i, j - 1) 两个格子走过来(不考虑边界条件),那么 vis[i][j] 是否可达的状态则可由如下公式计算得到:

vis[i][j]=vis[i−1][j] or vis[i][j−1]

即只要有一个格子可达,那么 (i, j) 这个格子就是可达的,因此我们只要遍历所有格子,递推计算出它们是否可达然后用变量 ans 记录可达的格子数量即可。

class Solution {
    int get(int x) {
        int res=0;
        for (; x; x /= 10){
            res += x % 10;
        }
        return res;
    }
public:
    int movingCount(int m, int n, int k) {
        if (!k) return 1;
        vector<vector<int> > vis(m, vector<int>(n, 0));
        int ans = 1;
        vis[0][0] = 1;
        for (int i = 0; i < m; ++i) {
            for (int j = 0; j < n; ++j) {
                if ((i == 0 && j == 0) || get(i) + get(j) > k) continue;
                // 边界判断
                if (i - 1 >= 0) vis[i][j] |= vis[i - 1][j];// | = 按位或且赋值运算符
                if (j - 1 >= 0) vis[i][j] |= vis[i][j - 1];
                ans += vis[i][j];
            }
        }
        return ans;
    }
};

复杂度分析:

  • 时间复杂度:O(mn),其中 m 为方格的行数, n 为方格的列数。一共有 O(mn)个状态需要计算,每个状态递推计算的时间复杂度为 O(1),所以总时间复杂度为 O(mn)。
  • 空间复杂度:O(mn),其中 m 为方格的行数,n 为方格的列数。我们需要 O(mn)大小的结构来记录每个位置是否可达。

参考

机器人的运动范围
图解 BFS + DFS
DFS: 字节题库 - #剑13 - 中等 - 机器人的运动范围 - 1刷

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值