Recursion 二维空间里机器人向右或向下走的所有路径问题 @CareerCup

这篇博客探讨了在二维空间中,机器人只能向右或向下移动的所有可能路径问题。通过回溯算法,博主强调在处理路径时,应当先将当前点加入路径,再根据子情况判断是否需要保留该点。同时比较了自己的解决方案与官方答案,认为自己的方法在判断自由移动点上更为直观。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这道题学到了在做回溯时,先把点加入到path中,然后根据子情况来决定success这个变量,从而决定之前加入的点是否有必要的。如果子情况返回的是false,则这个点也没有必要加入到path中,就可以移除了。

还有一个是,我的解答中每次函数只对当前点进行isFree的判断,官方答案中却对下一个节点进行判断。我还是觉得我的更直观一些。


package Recursion;

import java.awt.Point;
import java.util.ArrayList;
import java.util.Hashtable;

import CtCILibrary.AssortedMethods;

/**
 * Imagine a robot sitting on the upper left comer of an X by Ygrid. The robot
 * can only move in two directions: right and down. How many possible paths are
 * there for the robot to go from (0, 0) to (X, Y) ?
 * 
 * 在一个N*N矩阵的左上角坐着一个机器人,它只能向右运动或向下运动。那么, 机器人运动到右下角一共有多少种可能的路径?
进一步地,如果对于其中的一些格子,机器人是不能踏上去的。设计一种算法来获得所有可能的路径。
 * 
 */
public class S9_2 {

	public static int[][] maze = new int[10][10];
	
	public static void main(String[] args) {
		maze = AssortedMethods.randomMatrix(10, 10, 0, 4);
		AssortedMethods.printMatrix(maze);
		ArrayList<Point> path = new ArrayList<Point>();
		boolean success = getPathRec(9, 9, path);
		ArrayList<Point> pathRef = new ArrayList<Point>();
		boolean successRef = getPathRef(9, 9, pathRef);
		Hashtable<Point, Boolean> cache = new Hashtable<Point, Boolean>();
		ArrayList<Point> pathDP = new ArrayList<Point>();
		getPathMemo(9, 9, pathDP, cache);
		if (success) {
			String s = AssortedMethods.listOfPointsToString(path);
			System.out.println(s);
			System.out.println(AssortedMethods.listOfPointsToString(pathRef));
			System.out.println(AssortedMethods.listOfPointsToString(pathDP));
		} else {
			System.out.println("No path found.");
		}
	}
	
	// 返回是否成功获得一条路径
	public static boolean getPathRec(int x, int y, ArrayList<Point> path) {
		
		boolean free = isFree(x, y);		// 检查是否当前位置是合法的
		boolean success = false;			// 表示成功获得路径
		path.add(new Point(x, y));		// 可以采用先加入,如果后面的success为false则移除的方法!
		
		if(x==0 && y==0 && free){			// 到达初始点,找到路径
			return true;
		}
		
		if(x == 0 && free){					// 第一行
			success = getPathRec(x, y-1, path);
		}
		
		if(y == 0 && free){					// 第一列
			success = getPathRec(x-1, y, path);
		}
		
		if(x>0 && y>0 && free){			// 中间位置
			success = getPathRec(x-1, y, path);		// 先尝试往上走
			if( !success ){									// 如果往上走不得再尝试往左走
				success = getPathRec(x, y-1, path);
			}
		}
		
		if(!success){				// 如果所有尝试都失败了,证明当前节点不适合,因此移除!
			path.remove(path.size()-1);
		}
		return success;
	}
	
	public static boolean getPathMemo(int x, int y, ArrayList<Point> path, Hashtable<Point, Boolean> ht) {
		boolean free = isFree(x, y);		// 检查是否当前位置是合法的
		boolean success = false;			// 表示成功获得路径
		path.add(new Point(x, y));		// 可以采用先加入,如果后面的success为false则移除的方法!
		
		Point p = new Point(x, y);
		if(ht.contains(p)){					// 利用cache预先判断
			return ht.get(p);
		}
		
		if(x==0 && y==0 && free){			// 到达初始点,找到路径
			return true;
		}
		
		if(x == 0 && free){					// 第一行
			success = getPathMemo(x, y-1, path, ht);
		}
		
		if(y == 0 && free){					// 第一列
			success = getPathMemo(x-1, y, path, ht);
		}
		
		if(x>0 && y>0 && free){			// 中间位置
			success = getPathMemo(x-1, y, path, ht);		// 先尝试往上走
			if( !success ){									// 如果往上走不得再尝试往左走
				success = getPathMemo(x, y-1, path, ht);
			}
		}
		
		if(!success){				// 如果所有尝试都失败了,证明当前节点不适合,因此移除!
			path.remove(path.size()-1);
		}
		return success;
	}
	
	public static boolean isFree(int x, int y){
		return maze[x][y] != 0;
	}
	
	
	// 官方答案
	public static boolean getPathRef(int x, int y, ArrayList<Point> path) {
		Point p = new Point(x, y);
		path.add(p);
		if (x == 0 && y == 0) {
			return true; // found a path
		}
		boolean success = false;
		if (x >= 1 && isFree(x - 1, y)) { // Try right
			success = getPathRef(x - 1, y, path); // Free!  Go right
		}
		if (!success && y >= 1 && isFree(x, y - 1)) { // Try down
			success = getPathRef(x, y - 1, path); // Free!  Go down
		}
		if (!success) {
			path.remove(p); // Wrong way! Better stop going this way
		}
		return success;
	}

}


自顶向下二路归并排序是一种分治策略的典型应用,它将数组分为两半,分别对每半递归地进行排序,然后合并两个已排序的部分。以下是设计这种算法的一个简化步骤: 1. **划分阶段**(Divide):首先确定中间位置mid,将输入数组分为两个部分,左半部分从0到mid-1,右半部分从mid到数组长度减一。 2. **递归阶段**(Recursion):对于左右两个子数组,分别进行同样的操作,直到每个子数组只剩下一个元素,此时视为有序。 3. **合并阶段**(Combine):当两个子数组都处理完毕后,开始合并。创建一个新的临时数组,从两个子数组的第一个元素开始,比较它们的值,选择较小的那个放入新数组,并移动对应的指针。重复这个过程,直到其中一个子数组遍历完,再将另一个子数组剩余的部分复制到临时数组。 4. **回溯阶段**(Backtracking):随着合并过程,逐步将结果回填到原数组中,最终完成整个排序。 这是大体流程,下面是伪代码形式: ```python def merge_sort(arr): if len(arr) <= 1: return arr mid = len(arr) // 2 left_half = arr[:mid] right_half = arr[mid:] # 递归排序左右两个部分 left_half = merge_sort(left_half) right_half = merge_sort(right_half) # 合并两个有序部分 sorted_arr = merge(left_half, right_half) return sorted_arr # 合并函数 def merge(left, right): result = [] i = j = 0 while i < len(left) and j < len(right): if left[i] <= right[j]: result.append(left[i]) i += 1 else: result.append(right[j]) j += 1 result.extend(left[i:]) result.extend(right[j:]) return result ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值