程序员的算法趣题:Q27 禁止右转也没关系吗(Java版)

题目说明

在像日本这样车辆靠左通行的道路上,开车左转比右转要舒服些。
因为不用担心对面来车,所以只要一直靠左行驶,就不用思考怎么变道。
那么,现在来想一下如何只靠直行或者左转到达目的地。
假设在像图 15 一样的网状道路上,我们只能直行或者左转,
并且已经通过的道路就不能再次通过了。此时允许通行道路有交叉。
请思考一下从左下角去右上角时,满足条件的行驶路线共有多少种。
举个例子,如果是像 图 15 这样 3×2 的网状道路,则共有 4 种行驶路线。

Q27 3x2的走法

思路

1.定义四个方向。1-上; 2-左; 3-下; 4-右(注意,四个方向要符合连续左转的顺序,数字连续是为了方便地通过取余运算计算左转后的方向)
2.用两个二维数组hor,ver分别表示横向、竖向各个路线是否已经走过(0-未过;1-已过)
3.用坐标(x,y)表示当前在哪个交叉点上,
   此时:
          hor[y][x] 表示交叉点右侧的横线;
          ver[y][x] 表示交叉点上方的竖线。

Q27 交叉点与hor和ver的理解.png

代码

// 定义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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值