欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

剑指offer机器人运动范围 JZ66

程序员文章站 2022-07-10 18:30:10
/* * dfs(下一次) * 回溯 */public class Dfs_Move {/*四个方向 * x+dir[i] y+dir[i+1] * x+1 y+0 右 * x+0 y-1 上 * x-1 y+0 左 * x+0 y+1 下 * 想把右,下,左,上排列出来,但是尝试了下,会出现重复*/public int[] dir= {1, 0 ,-1 ,0 ,1};public static int count=0;//下表转化 如35转成5...

目录

本文是参考牛客网的官方题解结合自己理解写的,官方题解:牛客官方题解
其中官方题解采用C++,博主已入JAVA坑,理解官方题解意思,使用JAVA写的

题目描述

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

题目理解

给定一个矩阵的行和列row,columns和阈值threshold,从(0,0)出发,每次可以往上下左右四个方向走,并且走到(i,j)时,i和j的每位数之和需要小于等于threshold,问最多可以走多少格子。
对于这种遍历,我们首先可以想到深度优先搜索(DFS)或者广度优先搜索(BFS),当满足条件的进行标记,依次遍历完所有节点

方法一:DFS遍历

根据题目描述,我们可以模拟题目,我们假设一个5x5矩阵,阈值threshold=3,如果我们用DFS的话,就是按照某一个分支进行访问,而这种分支访问一般是采用递归的方式进行实现,对于DFS大家最好先理解递归,可以先看我上一篇剪绳子的题,就是一个很典型的递归.我在下面画了一个图,按照对应的方向进行搜索.
剑指offer机器人运动范围 JZ66
最开始,我们在(0,0)的位置,我们假设按照{右,上,左,下}的方向去试探。所以我们走的顺序应该是按照图中的下标走的。
当走到4的时候,发现不能往继续往右边走,并且4个方向都走不通了,那就回溯到3,发现可以走到5,接着就站在5的视角,发现可以走6,就一直按照这个想法。

本题的递归函数就是:首先站在(0,0)的视角,先往右试探,发现可以走,就以下一个为视角,继续做相同的事情

package JZ66_Robot_move;
import java.util.Scanner;
import java.util.Arrays;
/*
 * DFS深度优先,Depth First Search 对每一个可能的分之路径深入到不能深入为止
 * 且每个节点只能访问一次,同时做相应标记,当访问到该分支路径的末尾节点,就回溯到上一个节点,站在这个节点再进行四个方向的探索
 * 假设有一个5*5的矩阵,我们用DFS,就是一直按照一个方向走,假设我们按照{右,上,左,下}的方向试探,
 * 就是先按照向右一直搜索,当位置大于阈值就回溯到前面的位置在向下一直搜索
 * 第一步,检查下标
 *  第二步:检查是否被访问过,或者是否满足当前匹配条件
 *  第三步:检查是否满足返回结果条件
 *  第四步:都没有返回,说明应该进行下一步递归
 *  标记
 *  dfs(下一次)
 *   回溯
 */
public class Dfs_Move {
	/*四个方向
	 * x+dir[i+1] y+dir[i]
	 * x+0 y+1 右
	 * x-1 y+0 上
	 * x+0 y-1 左
	 * x+1 y+0 下
	 * 想把右,下,左,上排列出来,但是尝试了下,会出现重复
	*/
	public int[] dir= {1, 0 ,-1 ,0 ,1};
	public static  int  count=0;
	//下表转化 如35转成5+3=8
	public  int  check(int n) {
		int sum=0;
		while(n>0) {
			sum+=(n%10);
			n/=10;
		}
		return n;
	}
    public void  dfs(int x,int y,int threshold,int rows,int columns,int[][] mark) {
    	//检查下标
    	if(x<0||x>=rows||y<0||y>=columns) {
    		return ;
    	}
    	//必须分开,不然数组越界会报错
    	if(mark[x][y]==1) {
    		return ;
    	}
    	int sum_xy=check(x)+check(y);
    	//检查坐标之和是否大于阈值
    	if(sum_xy>threshold) {
    	    return ;
    	}
    	//此时经过检查 坐标[x] [y]是满足条件的
    	mark[x][y]=1;
    	count+=1;
    	//进行下一步的递归,按照右下左上的方向试探
    	for(int i=0;i<4;i++) {
    		dfs(x+dir[i+1],y+dir[i],threshold,rows,columns,mark);
    	}
    }
    public static void main(String[] args) {
    	System.out.print("Enter a row:"); 
    	Scanner sc=new Scanner(System.in);
        int rows=sc.nextInt();
        System.out.println("Enter a columns:");
        int columns=sc.nextInt();
        System.out.println("Enter a threshold:"); 
        int threshold=sc.nextInt();
        int[][] mark=new int[rows][columns];
        for(int i=0;i<rows;i++) {
        	for(int j=0;j<columns;j++) {
        		mark[i][j]=0;
        	}
        }
        Dfs_Move a=new Dfs_Move();
        a.dfs(0,0,threshold,rows,columns,mark);
        System.out.println(count);
    }
}

时间复杂度:O(mn), m,n为矩阵大小,每个元素最多访问过一次
空间复杂度:O(mn)

方法二:BFS遍历

当前图的遍历算法还有广度优先搜索(BFS),所以也可以用BFS做。方法一实例的图,用BFS就是如下这样:
剑指offer机器人运动范围 JZ66
广度优先

  • 1.顶点v入队列
  • 2.当队列非空时则继续执行,否则算法结束
  • 3.出队列取得队头顶点v;访问顶点v并标记顶点v已被访问。
  • 4.查找顶点v的一个邻接顶点close
  • 5.若colse未被访问过的,则colse入队列。
  • 6.继续查找顶点v的另一个新的邻接顶点new_col,转到步骤(5)。直到顶点v的所有未被访问过的邻接点处理完。转到步骤(2)
package JZ66_Robot_move;
import java.util.*;
/*广度优先
 * 1.顶点v入队列
 * 2.当队列非空时则继续执行,否则算法结束
 * 3.出队列取得队头顶点v;访问顶点v并标记顶点v已被访问。
 * 4.查找顶点v的一个邻接顶点close
 * 5.若colse未被访问过的,则colse入队列。
 * 6.继续查找顶点v的另一个新的邻接顶点new_col,转到步骤(5)。直到顶点v的所有未被访问过的邻接点处理完。转到步骤(2)。
 */
 //创建一个类似C++中的Pair结构,用于存放下标x,y
class Pair {
	private int meb_x;
	private int meb_y;
	public Pair(int x,int y) {
		this.meb_x=x;
		this.meb_y=y;
	}
	public int getX() {
		return meb_x;
	}
	public int getY() {
		return meb_y;
	}
}

public class Bfs_Move {
	private final int[] dir={1, 0, -1 , 0, 1};
	//下标转换
	public int check(int n) {
		int sum=0;
		while(n>0) {
			sum+=(n%10);
			n/=10;
		}
		return sum;
	}
	public static void main(String[] args) {
		System.out.print("Enter a row:"); 
    	Scanner sc=new Scanner(System.in);
        int rows=sc.nextInt();
        System.out.println("Enter a columns:");
        int columns=sc.nextInt();
        System.out.println("Enter a threshold:"); 
        int threshold=sc.nextInt();
        int[][] mark=new int[rows][columns];
        for(int i=0;i<rows;i++) {
        	for(int j=0;j<columns;j++) {
        		mark[i][j]=0;
        	}
        }
        int count=0;
        Bfs_Move bfs=new Bfs_Move();
        //创建一个队列,根据它的先进先出的特性,我们可以把顶点v中的所有近邻节点先存入,
        //然后一层一层按照先后顺序添加进队列中,实现广度遍历
        Queue<Pair> q=new LinkedList<Pair>();
        q.add(new Pair(0,0));
        mark[0][0]=1;
        count++;
        //只要队列一直不为空,就不结束循环
        while(!q.isEmpty()) {
        	//poll()会返回队列第一个元素,同时删除这个元素
        	Pair p=q.poll();
            //注意和DFS中的区别,DFS利用此循环进行递归,会一直先按照一个方向,比如此时的右,当访问到最后节点就回溯到之前按照其他方向一次探索
        	//在BFS中则是按照节点周围一圈先添加,在添加外面节点周围一圈
        	for(int i=0;i<4;i++) {
        		int x=p.getX()+bfs.dir[i+1];
        		int y=p.getY()+bfs.dir[i];
        		//可以观察 广度优先的遍历顺序
        		System.out.println("x y:"+x+" "+y);
        		int sum_xy=bfs.check(x)+bfs.check(y);
        		if(x>=0&&x<rows&&y>=0&&y<columns&&sum_xy<=threshold&&mark[x][y]!=1) {
        			count++;
        			mark[x][y]=1;
        			q.add(new Pair(x,y));
        		}
        	}
        }
        System.out.println(count);
	}
}

本文地址:https://blog.csdn.net/weixin_43568893/article/details/107877710