剑指offer13

地上有一个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。请问该机器人能够到达多少个格子?

分析:该题分析是一个矩阵的遍历过程,但是不可以直接使用两个嵌套for循环来遍历,求取满足条件的坐标点个数,因为存在着某些点满足条件,但是其周围的点都是不可达的,那么该点还是不可达。所以判断点是否可达应该一个点一个点的来看,我想到的是使用回溯法的方法,用stack来记录当前的坐标点,栈不为空就pop栈顶坐标,分析该坐标是否可达,之后分别分析将该点下移和右移是否满足条件(为啥只需要下移和右移后面分析,这个问题是我做这题的时候一个很难想的问题),满足则push入栈,否则跳过。其实我觉得也可以使用队列,也是可以的。

问题:为什么只需要看向下移动和向右移动,而不需要看向上移动和向左移动?题目中的数位之和记为f()

若相邻的三个数分别为x-1,x,x+1,一定只会x-1满足条件,其他x,x+1满足不满足均有可能,一定不会x-1不满足,x,x+1满足条件
当x+1不发生十位上的进位时,则f(x-1)<f(x)<f(x+1),当x-1不满足的时候,x,x+1肯定也不满足
当x+1发生个位上的进位时,f(x+1)+8 = f(x),f(x-1)+1 = f(x),所以可以发现解的情况应该是如上图所示的,从坐标领零点开始,下面一行的解总是比上一行的少一个,而且第一行的解也会在个位发生进位的时候发生突变,这样原本本来一直不满足的点,经过进位可能就满足了,但是尽管这样解也同样是一个上图的三角形,最好的情况也是第一行的两个三角形连起来,但这样同样只需要向下,向右遍历即可,若两个三角形的解不相连更不用说,我在做这题的时候这个地方困扰了我好久,开始一直想着应该四个方向,但是这样就会发生死循环,经过回过头的分析,以后做题想不通的要学会手动推导一下,不要仅仅画一些简单的示意图。

算法代码:

 1 class Solution {
 2 
 3     public int movingCount(int m,int n,int k){
 4         // 状态矩阵用来记录某一个点是否已经检测可达,可达标为1,默认为零,不可达为-1
 5         int[][] state = new int[m][n];
 6         int num = 0; // 记录可达的数量
 7 
 8         // 定义初始的坐标x,y变量
 9         int x=0,y=0;
10         Stack<int[]> stack = new Stack();
11         stack.push(new int[]{x,y});
12 
13         while(!stack.empty()){
14             int[] temp = stack.pop();
15             x = temp[0];y = temp[1];
16             // 判断pop出的坐标是否符合,不符合就不用看向前后左右移动
17             if(isOk(temp[0],temp[1],k)){
18                 if(state[x][y] != 1) {num++; state[x][y] = 1;}
19                 // 向下,如何合规就push
20                 if(x+1<m&&isOk(x+1,y,k)&&state[x+1][y]==0) stack.push(new int[]{x+1,y});
21 
22                 // 向右,如果合规就push
23                 if(y+1<n&&isOk(x,y+1,k)&&state[x][y+1]==0) stack.push(new int[]{x,y+1});
24             }
25         }
26 
27         return num;
28     }
29 
30 
31     // 判断(x,y)是否是合法的位置
32     public static boolean isOk(int x,int y,int k){
33         int sum = 0;
34         while(x>=10){sum += x%10; x/=10;}
35         while(y>=10){sum += y%10; y/=10;}
36 
37         return sum+x+y<=k?true:false;
38     }
39 }
知之为知之,不知为不知
原文地址:https://www.cnblogs.com/bevishe/p/12416807.html