罗密欧与朱丽叶的迷宫问题(java版带有详细分析与注释)

罗密欧与朱丽叶的迷宫问题

问题描述

罗密欧与朱丽叶的迷宫问题

罗密欧与朱丽叶身处一个 $m×n $的迷宫中,如图所示。每一个方格表示迷宫中的一个房间。这 m × n m×n m×n 个房间中有一些房间是封闭的,不允许任何人进入。在迷宫中任何位置均可沿 8 个方向进入未封闭的房间。罗密欧位于迷宫的 ( p , q ) (p,q) (pq)方格中,他必须找出一条通向朱丽叶所在的 ( r , s ) (r,s) (rs)方格的路。在抵达朱丽叶之前,他必须走遍所有未封闭的房间各一次,而且要使到达朱丽叶的转弯次数为最少。每改变一次前进方向算作转弯一次。请设计一个算法帮助罗密欧找出这样一条道路。

image-20211211230356770.png

算法分析与设计

分析

对于给定的罗密欧与朱丽叶的迷宫,编程计算罗密欧通向朱丽叶的所有最少转弯道路。

整体思路:
  1. 首先需要先处理输入,记录封闭房间,两人的位置
  2. 回溯法查找最优路线
  3. 输出结果
回溯法细节

框架可以直接套用课上讲的回溯法的基本框架

  • 判断结束的条件为:如果所有方格走遍,且到达朱丽叶位置,且转弯次数<=最优转弯次数
    • 当前路线的转弯次数小于最优的转弯次数的话需要更新记录值
    • 否则只需要将路线数+1即可
  • 然后需要遍历八个可走的方向进行回溯,在每个方向上均需要计算出下一步的坐标,然后判断下一步是否满足约束函数
    • 如果满足就进入下一步继续回溯
    • 如果不满足则跳过判断下一个方向是否可走
函数解释
  • 函数 void save()保存找到的最优路线;
  • 函数boolean ok(int x, int y)用于判断是否越界和可通过;
  • 函数void output()用于输出结果;
  • 函数void backtrack(int x, int y, int dep, int di)用于回溯求解

代码说明

Solution.java

package edu.xjtu.work5_12;

import java.util.Scanner;

public class Solution {
    static final int[] dx = {1, 0, -1, 0, 1, 1, -1, -1}; //八个方向
    static final int[] dy = {0, 1, 0, -1, 1, -1, 1, -1};
    static Point luo = new Point();// 罗密欧位置
    static Point ye = new Point();// 朱丽叶位置
    static int MAX = 10;
    static final int[][] board = new int[MAX][MAX];
    static final int[][] best = new int[MAX][MAX];
    static int n, m, k;
    static int dirs = 0;  //转弯次数
    static int min = 100000;   //最少转弯次数
    static int count = 0;  //不同的最少转弯道路数

    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        n = input.nextInt();// 读入行数
        m = input.nextInt();// 读入列数
        k = input.nextInt();// 封闭房间数

        int x, y;

        // 封闭房间所在的行号和列号
        for (int i = 1; i <= k; i++) {
            x = input.nextInt();
            y = input.nextInt();
            board[x][y] = -1;  //标记封闭房间
        }

        // 罗密欧的位置
        luo.x = input.nextInt();
        luo.y = input.nextInt();

        // 朱丽叶的位置
        ye.x = input.nextInt();
        ye.y = input.nextInt();
        input.close();

        board[luo.x][luo.y] = 1;
        backtrack(luo.x, luo.y, 1, 0);// 回溯法求解

        output();// 输出结果

    }


    /**
     * 回溯查找结果
     *
     * @param x   坐标x
     * @param y   坐标y
     * @param dep 总深度
     * @param di  当前深度
     */
    static void backtrack(int x, int y, int dep, int di) {
        //如果所有方格走遍,且到达朱丽叶位置,且转弯次数<=最优转弯次数
        if (dep == n * m - k && x == ye.x && y == ye.y && dirs <= min) {         
            if (dirs < min)  //如果<最优转弯次数
            {
                min = dirs;
                count = 1;   //重新记录道路数
                save();
            } else
                count++;  //否则道路数增加
            return;
        }
        if (dep == n * m - k && x == ye.x && y == ye.y && dirs > min)
            return;
        for (int i = 0; i < 8; i++)  //可走的八个方向
        {
            int nextx = x + dx[i];  //计算下一步坐标
            int nexty = y + dy[i];
            if (ok(nextx, nexty))  //如果下一步可行
            {
                board[nextx][nexty] = dep + 1;
                if (dep > 1 && di != i)  //第一步不算转弯,如果方向不同,转弯次数增加
                    dirs++;

                backtrack(nextx, nexty, dep + 1, i);

                board[nextx][nexty] = 0;  //回溯
                if (dep > 1 && di != i)
                    dirs--;
            }
        }
    }

    /**
     * 输出结果
     */
    static void output() {
        System.out.println(min);
        System.out.println(count);
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m; j++)
                System.out.print(best[i][j] + "\t");
            System.out.println();
        }
    }

    /**
     * 判断是否满足约束函数
     *
     * @param x 坐标x
     * @param y 坐标y
     * @return 是否满足
     */
    static boolean ok(int x, int y) {
        //在迷宫内且未走到封闭房间
        return x > 0 && y > 0 && x <= n && y <= m && board[x][y] == 0;
    }

    /**
     * 保存最优解
     */
    static void save() {
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= m; j++)
                best[i][j] = board[i][j];
    }

}

Point.java

package edu.xjtu.work5_12;

public class Point {
    int x;
    int y;
}

运行结果展示

image-20211211235035248.png

  • 4
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hydrion-Qlz

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值