四重优化,速看剑指 Offer 13. 机器人的运动范围
Posted 来老铁干了这碗代码
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了四重优化,速看剑指 Offer 13. 机器人的运动范围相关的知识,希望对你有一定的参考价值。
立志用最少的代码做最高效的表达
题目
地上有一个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
第一次解题
错误解法:直接遍历二维数组试图解题,发现出错,因为机器人无法越过障碍
class Solution {
public int movingCount(int m, int n, int k) {
int num1, num2, sum = 0;
for(int i = 0; i < m; i++) {
for(int j = 0; j < n; j++) {
num1 = 0; num2 = 0;
int ii = i, jj = j;
while(ii != 0) { num1 += ii%10; ii/= 10; }
while(jj != 0) { num2 += jj%10; jj/= 10; }
if(num1 + num2 <= k) sum++;
System.out.println(i + " " + j + " " + num1 + " " + num2 + " " + sum);
}
}
return sum;
}
}
第二次解题
仔细思考后发现,这是一道考查连通块的题,即连通(0,0)的点的个数。
于是用DFS解,如下:
static class Solution {
boolean[][] vis;
int sum;
int kk; // 代替k,定义全局变量,可以为dfs方法减少一个参数
int[][] next;
int mm, nn;
public int movingCount(int m, int n, int k) {
// 全局变量的定义
vis = new boolean[m][n];
sum = 1; // (0,0)一定能被访问到
kk = k; mm = m; nn = n;
next = new int[][]{{1,0}, {-1,0}, {0,1}, {0,-1}};
vis[0][0] = true;
dfs(0, 0);
return sum;
}
public void dfs(int x, int y) {
// 终止条件
if(x > mm || y > nn) return;
// 回溯
for(int i = 0; i < 4; i++) {
int xx = x + next[i][0], yy = y + next[i][1];
// 1、超界, continue
if(xx < 0 || xx >= mm || yy < 0 || yy >= nn) continue;
// 2、被访问过,continue
if(vis[xx][yy]) continue;
// 3、计算是否能到达
int xx1 = xx, yy1 = yy;
int num1 = 0, num2 = 0;
while(xx1 != 0) { num1 += xx1%10; xx1 /= 10; }
while(yy1 != 0) { num2 += yy1%10; yy1 /= 10; }
if(num1 + num2 <= kk) { // 符合
vis[xx][yy] = true;
sum++;
dfs(xx, yy);
}
}
}
}
第三次优化
解法2中考虑了m或n是100的情况,但即使m or n为100,k最大值也只能取到20,因此不满足条件。 因此只需考虑m or n是二位数的情况即可。
class Solution {
boolean[][] vis;
int sum;
int kk; // 代替k,定义全局变量,可以为dfs方法减少一个参数
int[][] next;
int mm, nn;
public int movingCount(int m, int n, int k) {
// 全局变量的定义
vis = new boolean[m][n];
sum = 1; // (0,0)一定能被访问到
kk = k; mm = m; nn = n;
next = new int[][]{{1,0}, {-1,0}, {0,1}, {0,-1}};
vis[0][0] = true;
dfs(0, 0);
return sum;
}
public void dfs(int x, int y) {
// 终止条件
if(x > mm || y > nn) return;
// 回溯
for(int i = 0; i < 4; i++) {
int xx = x + next[i][0], yy = y + next[i][1];
// 1、是否超界 or 是否满足条件, continue
if(xx < 0 || xx >= mm || yy < 0 || yy >= nn || (xx%10 + xx/10 + yy%10 + yy/10 > kk)) continue;
// 2、是否曾经走过这个点
if(!vis[xx][yy]) {
vis[xx][yy] = true;
sum++;
dfs(xx, yy);
}
}
}
}
第四次优化
第三次优化中,考虑的机器人从上下左右四个方向走的问题,但仔细思考后发现:
由于从0,0开始,因此只考虑向右or向下走即可。
class Solution {
boolean[][] vis;
int sum;
int kk; // 代替k,定义全局变量,可以为dfs方法减少一个参数
int[][] next;
int mm, nn;
public int movingCount(int m, int n, int k) {
// 全局变量的定义
vis = new boolean[m][n];
sum = 1; // (0,0)一定能被访问到
kk = k; mm = m; nn = n;
next = new int[][]{{1,0}, {0,1}};
vis[0][0] = true;
dfs(0, 0);
return sum;
}
public void dfs(int x, int y) {
// 终止条件
if(x > mm || y > nn) return;
// 回溯
for(int i = 0; i < 2; i++) {
int xx = x + next[i][0], yy = y + next[i][1];
// 1、是否超界 or 是否满足条件, continue
if(xx < 0 || xx >= mm || yy < 0 || yy >= nn || (xx%10 + xx/10 + yy%10 + yy/10 > kk)) continue;
// 2、是否曾经走过这个点
if(!vis[xx][yy]) {
vis[xx][yy] = true;
sum++;
dfs(xx, yy);
}
}
}
}
完整可运行代码
最后附上完整可运行代码
public class 剑指Offer13_机器人的运动范围 {
// 错误解法: 因为无法越过障碍
static class Solution_1 {
public int movingCount(int m, int n, int k) {
int num1, num2, sum = 0;
for(int i = 0; i < m; i++) {
for(int j = 0; j < n; j++) {
num1 = 0; num2 = 0;
int ii = i, jj = j;
while(ii != 0) { num1 += ii%10; ii/= 10; }
while(jj != 0) { num2 += jj%10; jj/= 10; }
if(num1 + num2 <= k) sum++;
System.out.println(i + " " + j + " " + num1 + " " + num2 + " " + sum);
}
}
return sum;
}
}
// 解法2:dfs:本质上是求连通块,就是连通(0,0)点的所有点的个数S
static class Solution_2 {
boolean[][] vis;
int sum;
int kk; // 代替k,定义全局变量,可以为dfs方法减少一个参数
int[][] next;
int mm, nn;
public int movingCount(int m, int n, int k) {
// 全局变量的定义
vis = new boolean[m][n];
sum = 1; // (0,0)一定能被访问到
kk = k; mm = m; nn = n;
next = new int[][]{{1,0}, {-1,0}, {0,1}, {0,-1}};
vis[0][0] = true;
dfs(0, 0);
return sum;
}
public void dfs(int x, int y) {
// 终止条件
if(x > mm || y > nn) return;
// 回溯
for(int i = 0; i < 4; i++) {
int xx = x + next[i][0], yy = y + next[i][1];
// 1、超界, continue
if(xx < 0 || xx >= mm || yy < 0 || yy >= nn) continue;
// 2、被访问过,continue
if(vis[xx][yy]) continue;
// 3、计算是否能到达
int xx1 = xx, yy1 = yy;
int num1 = 0, num2 = 0;
while(xx1 != 0) { num1 += xx1%10; xx1 /= 10; }
while(yy1 != 0) { num2 += yy1%10; yy1 /= 10; }
if(num1 + num2 <= kk) { // 符合
vis[xx][yy] = true;
sum++;
dfs(xx, yy);
}
}
}
}
// 解法2中考虑的m或n是100的情况,但即使m or n为100,k最大值也只能取到20,因此不满足条件。 因此只需考虑m or n是二位数的情况即可。
static class Solution_3 {
boolean[][] vis;
int sum;
int kk; // 代替k,定义全局变量,可以为dfs方法减少一个参数
int[][] next;
int mm, nn;
public int movingCount(int m, int n, int k) {
// 全局变量的定义
vis = new boolean[m][n];
sum = 1; // (0,0)一定能被访问到
kk = k; mm = m; nn = n;
next = new int[][]{{1,0}, {-1,0}, {0,1}, {0,-1}};
vis[0][0] = true;
dfs(0, 0);
return sum;
}
public void dfs(int x, int y) {
// 终止条件
if(x > mm || y > nn) return;
// 回溯
for(int i = 0; i < 4; i++) {
int xx = x + next[i][0], yy = y + next[i][1];
// 1、是否超界 or 是否满足条件, continue
if(xx < 0 || xx >= mm || yy < 0 || yy >= nn || (xx%10 + xx/10 + yy%10 + yy/10 > kk)) continue;
// 2、是否曾经走过这个点
if(!vis[xx][yy]) {
vis[xx][yy] = true;
sum++;
dfs(xx, yy);
}
}
}
}
// 解法4:由于从0,0开始,因此只考虑向右or向下走即可。
static class Solution_4 {
boolean[][] vis;
int sum;
int kk; // 代替k,定义全局变量,可以为dfs方法减少一个参数
int[][] next;
int mm, nn;
public int movingCount(int m, int n, int k) {
// 全局变量的定义
vis = new boolean[m][n];
sum = 1; // (0,0)一定能被访问到
kk = k; mm = m; nn = n;
next = new int[][]{{1,0以上是关于四重优化,速看剑指 Offer 13. 机器人的运动范围的主要内容,如果未能解决你的问题,请参考以下文章
剑指 Offer(C++版本)系列:剑指 Offer 13 机器人的运动范围
剑指 Offer(C++版本)系列:剑指 Offer 13 机器人的运动范围
剑指 Offer(C++版本)系列:剑指 Offer 13 机器人的运动范围