剑指offer--机器人的运动范围

题目描述

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

题意分析:

题目是指从坐标0,0开始,能够到达的格子数目的总和。可以理解为从坐标0,0开始倒水,谁能够漫过的格子的数目的总和。因为有限制条件的存在,因此水不能漫过所有格子。

要注意题意的理解偏差,不是以下题意:

  • 求最长路径,不是求机器人能走到的最长路径
  • 求所有路径,不是求机器人有多少种走法
解法:

简单而言,就是广度遍历,并且已经遍历过的格子,可以不遍历。
我们假设有一个方法moveCount(i,j)能够求得从坐标(i,j)开始,能够到达的格子数,那么

moveCount(i,j) = 它四周格子能够到达的格子数 + 1(本身)

另外,不能访问的格子,有三种情况

  • 超出边界
  • 不满足题目条件k
  • 已经计算过,或者说访问过

因此给出递归解法:

import java.util.Stack;
public class Solution {
    int movingCount(int threshold, int rows, int cols){
        int flag[][] = new int[rows][cols]; //记录是否已经走过
        return moveCount(0, 0, rows, cols, flag, threshold);
    }

    /**
    ** 计算某个位置开始,能到达多少个格子
    **/
    private int moveCount(int i, int j, int rows, int cols, int[][] flag, int threshold) {
        //超出边界的,不能到达
        if (i < 0 || i >= rows || j < 0 || j >= cols){
            return 0;
        }
        //已经到达过的,不能到达
        if(flag[i][j] == 1){           
            return 0;
        }
        //不满足位置条件的,不能到达
        if(numSum(i) + numSum(j)  > threshold){
            return 0;
        }            
        flag[i][j] = 1;
        //对于一个起始点(x,y),它能到达的数目,等于他四个方向能到达数目的和
        return moveCount(i - 1, j, rows, cols, flag, threshold)
            + moveCount(i + 1, j, rows, cols, flag, threshold)
            + moveCount(i, j - 1, rows, cols, flag, threshold)
            + moveCount(i, j + 1, rows, cols, flag, threshold)
            + 1;
    }

    /**
    ** 计算位置条件
    **/
    private int numSum(int i) {
        int sum = 0;
        do{
            sum += i%10;
        }while((i = i/10) > 0);
        return sum;
    }
}

非递归算法:
使用栈进行遍历

import java.util.Stack;
public class Solution {
    int movingCount(int threshold, int rows, int cols){
        if(threshold<0){
            return 0;
        }
        int flag[][] = new int[rows][cols]; //记录是否已经走过
        Stack<Integer> stackX = new Stack<Integer>();
        Stack<Integer> stackY = new Stack<Integer>();
        stackX.push(0);
        stackY.push(0);
        flag[0][0] = 1;
        int count = 1;
        while(!stackX.isEmpty()){
            int x = stackX.peek();
            int y = stackY.peek();
            int nextX = 0;
            int nextY = 0;
            //四个方向
            if(isSafe(nextX = x-1,nextY = y,rows, cols, flag,threshold)
              || isSafe(nextX = x+1,nextY = y,rows, cols, flag,threshold)
              || isSafe(nextX = x,nextY = y-1,rows, cols, flag,threshold)
              || isSafe(nextX = x,nextY = y+1,rows, cols, flag,threshold)){
                stackX.push(nextX);
                stackY.push(nextY);
                flag[nextX][nextY] = 1;
                count++;
                continue;
            }                   
            stackX.pop();
            stackY.pop();
        }
        return count;
    }

    private boolean isSafe(int i, int j, int rows, int cols, int[][] flag, int threshold){
        //超出边界的,不能到达
        if (i < 0 || i >= rows || j < 0 || j >= cols){
            return false;
        }
        //已经到达过的,不能到达
        if(flag[i][j] == 1){           
            return false;
        }
        //不满足位置条件的,不能到达
        if(numSum(i) + numSum(j)  > threshold){
            return false;
        }
        return true;
    }

    /**
    ** 计算位置条件
    **/
    private int numSum(int i) {
        int sum = 0;
        do{
            sum += i%10;
        }while((i = i/10) > 0);
        return sum;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值