题目说明
在像日本这样车辆靠左通行的道路上,开车左转比右转要舒服些。
因为不用担心对面来车,所以只要一直靠左行驶,就不用思考怎么变道。
那么,现在来想一下如何只靠直行或者左转到达目的地。
假设在像图 15 一样的网状道路上,我们只能直行或者左转,
并且已经通过的道路就不能再次通过了。此时允许通行道路有交叉。
请思考一下从左下角去右上角时,满足条件的行驶路线共有多少种。
举个例子,如果是像 图 15 这样 3×2 的网状道路,则共有 4 种行驶路线。
思路
1.定义四个方向。1-上; 2-左; 3-下; 4-右(注意,四个方向要符合连续左转的顺序,数字连续是为了方便地通过取余运算计算左转后的方向)
2.用两个二维数组hor,ver分别表示横向、竖向各个路线是否已经走过(0-未过;1-已过)
3.用坐标(x,y)表示当前在哪个交叉点上,
此时:
hor[y][x] 表示交叉点右侧的横线;
ver[y][x] 表示交叉点上方的竖线。
代码
// 定义4个方向
private static final int UP = 1;
private static final int LEFT = 2;
private static final int DOWN = 3;
private static final int RIGHT = 4;
private static final int W = 6; // 宽度为6个格
private static final int H = 4; // 高度为4个格
private static int count = 0; // 统计到达终点的路线数
private static List<String> steps = new LinkedList<String>(); // 仅用于展示每一种到达终点的走法
public static void main(String[] args) {
int[][] hor = new int[H+1][W]; // 横向路线 0-未经过;1-已经过
int[][] ver = new int[H][W+1]; // 竖向路线 0-未经过;1-已经过
// 从起点(0,0)开始移动,起步的朝向是右侧。(起步朝向只有上or右。如果选择上,则直行到左上角就废掉了)
move(0,0, RIGHT, hor, ver, steps);
System.out.println(count); // 2760
}
/**
* 移动一步:直行or左转
* @param x 当前(移动前)所在交叉点的x坐标 以左下角为起点,范围[0,W]
* @param y 当前(移动前)所在交叉点的y坐标 以左下角为起点,范围[0,H]
* @param curDir 当前的朝向(也就是上一步的移动方向)
* @param hor 横向路线通过情况(不可重复通过某一段路线)
* @param ver 竖向路线通过情况(不可重复通过某一段路线)
*/
private static void move(int x, int y, int curDir, int[][] hor, int[][] ver, List<String> steps) {
if(x < 0 || y < 0 || x > W || y > H) return ; // 坐标越界
if(x == W && y == H){ // 到达终点
count ++;
System.out.println(steps);
return ;
}
// 当下的第一种选择:直行
goStraight(x, y, curDir, hor, ver, steps);
// 当下的第二种选择:左转
turnLeft(x, y, curDir, hor, ver, steps);
}
// 直行
private static void goStraight(int x, int y, int curDir, int[][] hor, int[][] ver, List<String> steps) {
if(x < 0 || y < 0 || x > W || y > H) return ; // 坐标越界
if(curDir == UP && y > H-1) return; // 避免ver[y][-]越界
if(curDir == LEFT && x - 1 < 0) return;
if(curDir == DOWN && y - 1 < 0) return;
if(curDir == RIGHT && x > W-1) return; // 避免hor[-][x]越界
// 交叉点上边的路线是否已经走过
if(curDir == UP && ver[y][x] == 0){
ver[y][x] = 1;
steps.add("上");
move(x, y+1, curDir, hor, ver, steps);
steps.remove(steps.size()-1); // 还原
ver[y][x] = 0; // 还原
}
// 交叉点左边的路线是否已经走过
if(curDir == LEFT && hor[y][x-1] == 0){
hor[y][x-1] = 1;
steps.add("左");
move(x-1, y, curDir, hor, ver, steps);
steps.remove(steps.size()-1);
hor[y][x-1] = 0;
}
// 交叉点下边的路线是否已经走过
if(curDir == DOWN && ver[y-1][x] == 0){
ver[y-1][x] = 1;
steps.add("下");
move(x, y-1, curDir, hor, ver, steps);
steps.remove(steps.size()-1);
ver[y-1][x] = 0;
}
// 交叉点右边的路线是否已经走过
if(curDir == RIGHT && hor[y][x] == 0){
hor[y][x] = 1;
steps.add("右");
move(x+1, y, curDir, hor, ver, steps);
steps.remove(steps.size()-1);
hor[y][x] = 0;
}
}
// 左转
private static void turnLeft(int x, int y, int curDir, int[][] hor, int[][] ver, List<String> steps) {
if(x < 0 || y < 0 || x > W || y > H) return ; // 坐标越界
if(curDir == UP && x - 1 < 0) return;
if(curDir == LEFT && y - 1 < 0) return;
if(curDir == DOWN && x > W-1) return;
if(curDir == RIGHT && y > H-1) return;
// 左转:交叉点上边的路线是否已经走过
if(curDir == RIGHT && ver[y][x] == 0){
ver[y][x] = 1;
steps.add("上");
move(x, y+1, curDir % 4 + 1, hor, ver, steps);
steps.remove(steps.size()-1); // 还原
ver[y][x] = 0; // 还原
}
// 左转:交叉点左边的路线是否已经走过
if(curDir == UP && hor[y][x-1] == 0){
hor[y][x-1] = 1;
steps.add("左");
move(x-1, y, curDir % 4 + 1, hor, ver, steps);
steps.remove(steps.size()-1);
hor[y][x-1] = 0;
}
// 左转:交叉点下边的路线是否已经走过
if(curDir == LEFT && ver[y-1][x] == 0){
ver[y-1][x] = 1;
steps.add("下");
move(x, y-1, curDir % 4 + 1, hor, ver, steps);
steps.remove(steps.size()-1);
ver[y-1][x] = 0;
}
// 左转:交叉点右边的路线是否已经走过
if(curDir == DOWN && hor[y][x] == 0){
hor[y][x] = 1;
steps.add("右");
move(x+1, y, curDir % 4 + 1, hor, ver, steps);
steps.remove(steps.size()-1);
hor[y][x] = 0;
}
}
结果
[右, 右, 右, 右, 右, 右, 上, 上, 上, 上]
......此处省略很多条数据......
[右, 上, 上, 左, 下, 右, 右, 上, 上, 左, 下, 右, 右, 上, 上, 左, 下, 右, 右, 右, 上, 左, 下, 下, 右, 右, 上, 上]
2760