算法面试题-美团校招题(拜访)

题目很简单,注意方向只能选左右中的一个和上下中的一个,既然要走到目的地,那么方向已经决定了,就是尽量靠近目的地。

分析:红色的圈表示起点,红色三角形表示终点。这里如果向上走或者向右走,那么永远到不了终点,所以路径只会在两个点构成的矩形中,并且方向只有两个。

按照上图,假设向左走,走一步到绿色圈的位置,或者向下走一步到蓝色的圈的位置(这里暂时假设没有障碍),对于这两种走法,都会形成一个新的移动矩形(用不同颜色表示),子问题的解组成原问题的解,动态规划。

设水平和垂直移动方向分别为v(左移为-1,右移为1,有障碍为0)和h,从起点坐标(x,y)到终点坐标(desX,desY)的方案数为f(x,y),则有下面状态转移方程:

有了这个,代码就很容易了:

 1     public int countPath(int[][] map, int n, int m) {
 2         int x = 0, y = 0, desX = 0, desY = 0;
 3         int vertical;
 4         int horizontal;
 5         for (int i = 0; i < n; i++) { // 搜索起点和终点
 6             for (int j = 0; j < m; j++) {
 7                 if (map[i][j] == 1) {
 8                     x = i;
 9                     y = j;
10                 }
11                 if (map[i][j] == 2) {
12                     desX = i;
13                     desY = j;
14                 }
15             }
16         }
17         if (desX == x) { // 设定递归终止条件
18             for (int i = Math.min(y, desY); i <= Math.max(y, desY); i++) {
19                 if (map[x][i] == -1) { // 直线路径上是否有障碍
20                     return 0;
21                 }
22             }
23             return 1;
24         }
25         if (desY == y) {
26             for (int i = Math.min(x, desX); i < Math.max(x, desX); i++) {
27                 if (map[i][y] == -1) {
28                     return 0;
29                 }
30             }
31             return 1;
32         }
33         horizontal = desX - x > 0 ? 1 : -1; // 确定水平方向
34         vertical = desY - y > 0 ? 1 : -1; // 确定垂直方向
35         int[][] ver = new int[map.length][];
36         for (int i = 0; i < ver.length; i++) {
37             ver[i] = map[i].clone(); // 逐层拷贝,否则引用会和下面的数组相同
38         }
39         ver[x][y] = 0;
40         ver[x + vertical][y] = 1;
41         int[][] hor = new int[map.length][];
42         for (int i = 0; i < hor.length; i++) {
43             hor[i] = map[i].clone();
44         }
45         hor[x][y] = 0;
46         hor[x][y + horizontal] = 1;
47         if (map[x + vertical][y] == -1) { // 判断水平路径是否合法,即是否能水平移动
48             return countPath(hor, n, m); // 如不能,则只垂直移动
49         }
50         if (map[x][y + horizontal] == -1) {
51             return countPath(ver, n, m);
52         }
53         return countPath(ver, n, m) + countPath(hor, n, m); // 如果方向没限制,则结果为水平走和垂直的和
54     }

因为要把移动了之后的数组传入递归方法,所以需要对数组进行clone操作,这里不能直接对二维数组进行clone操作,需要通过for循环逐层拷贝,否则会因为引用相同而使得ver和hor数组的内容相同。

原文地址:https://www.cnblogs.com/Fndroid/p/6180132.html