zoukankan      html  css  js  c++  java
  • 啊哈算法之水管工游戏

    简述

    本算法摘选自啊哈磊所著的《啊哈!算法》第四章第六节的题目——水管工游戏。文中代码使用C语言编写,博主通过阅读和理解,重新由Java代码实现了一遍。

    游戏介绍

    游戏内容介绍在原文中表述详细,并附带了图片加以说明,同时给出了解答思路,这里有点需要说明的是,原文中作者为了方便读者理解,特地将开始位置设置为了(1, 1),而在我用java改写之后,就恢复到了数组中以原点(0, 0)来表示了,不冲突,只是在阅读原文思路分析和博主代码对比时需要注意这一点差异。因为原文篇幅较大,而且说明的内容较为丰富,在此就直接引荐原文内容了。

    (以上图片来源于原文截图)

    代码实现

    更多关于上文分析内容,均可以在以下的代码实现的注释中得到答案。

      1 /**
      2  * @Project: dailypro
      3  * @PackageName: com.captainad.algorithm
      4  * @Author: Captain&D
      5  * @Website: https://www.cnblogs.com/captainad/
      6  * @DateTime: 2019/6/21 12:00.
      7  * @Description: 水管工游戏
      8  */
      9 public class PlumberGame {
     10 
     11     /**
     12      * 自定义内部类,代表管道所处节点
     13      */
     14     static class PlumberNode {
     15         /** 横坐标 */
     16         int x;
     17 
     18         /** 纵坐标 */
     19         int y;
     20 
     21         public PlumberNode(int x, int y) {
     22             this.x = x;
     23             this.y = y;
     24         }
     25     }
     26 
     27     /**
     28      * 自定义内部类,表示栈,用来记录铺设管道的路径
     29      */
     30     static class PlumberStack {
     31         /** 数据栈 */
     32         PlumberNode[] data = new PlumberNode[100];
     33 
     34         /** 栈顶指针,初始值为0 */
     35         int top = 0;
     36     }
     37 
     38     /** 二维数组模拟水管铺设地图 */
     39     private static int[][] map = new int[10][10];
     40 
     41     /** 桶,标记已经铺设过的点位 */
     42     private static int[][] book = new int[10][10];
     43 
     44     /** 地图边界,以及是否到达重点的标记 */
     45     private static int m = 0, n = 0, flag = 0;
     46 
     47     /**
     48      * 递归处理每一个单元格位置所能处理的管道摆放方式
     49      * @param x 单元格横坐标
     50      * @param y 单元格纵坐标
     51      * @param front 当前单元格中进水口方向
     52      * @param stack 表示铺设管道记录铺设路径的栈
     53      */
     54     public static void dfs(int x, int y, int front, PlumberStack stack) {
     55 
     56         // 判断是否以及到达终点位置,其中xy都是从0开始计算。
     57         // 这里因为最后的出水口在地图外面,所以如果到达最后地图外的那个点位,则说明管道铺设完成
     58         if(x == (m - 1) && y == n) {
     59 
     60             // 更新完成管道的铺设标记
     61             flag = 1;
     62 
     63             // 找到铺设方案,打印铺设轨迹
     64             for(int i = 0; i < stack.top; i++) {
     65                 System.out.print(String.format("(%d, %d) ", stack.data[i].x + 1, stack.data[i].y + 1));
     66             }
     67 
     68             // 到此返回
     69             return;
     70         }
     71 
     72         // 判断是否越界,注意如果有上面这层判断,越界判断就得放在下面
     73         if(x < 0 || x >= m || y < 0 || y >= n) {
     74             return;
     75         }
     76 
     77         // 判断当前点位管道是否已经使用过,使用过则掠过,没有使用则继续并加入到桶中标记
     78         if(book[x][y] == 1) {
     79             return;
     80         }
     81         book[x][y] = 1;
     82 
     83         // 将当前尝试的坐标入栈,然后栈顶指针上移一位
     84         PlumberNode node = new PlumberNode(x, y);
     85         stack.data[stack.top] = node;
     86         stack.top++;
     87 
     88         // 当当前管道是直管的情况
     89         if(map[x][y] >= 5 && map[x][y] <= 6) {
     90 
     91             // 进水口在左边的情况
     92             if(front == 1) {
     93                 // 此时只能使用5号这种摆放方式,且下次进水口也是在左边
     94                 dfs(x, y+1, 1, stack);
     95             }
     96 
     97             // 进水口在上边的情况
     98             if(front == 2) {
     99                 // 此时只能使用6号这种摆放方式,且下次进水口也是在上边
    100                 dfs(x+1, y, 2, stack);
    101             }
    102 
    103             // 进水口在右边的情况
    104             if(front == 3) {
    105                 // 此时只能使用5号这种摆放方式,且下次进水口也是在右边
    106                 dfs(x, y - 1, 3, stack);
    107             }
    108 
    109             // 进水口在下边的情况
    110             if(front == 4) {
    111                 // 此时只能使用6号这种摆放方式,且下次进水口也是在下边
    112                 dfs(x - 1, y, 4, stack);
    113             }
    114 
    115         }
    116 
    117         // 当当前管道是弯管时
    118         if(map[x][y] >= 1 && map[x][y] <= 4) {
    119 
    120             // 进水口在左边的情况
    121             if(front == 1) {
    122 
    123                 // 此时可以有3,4号两种摆放方式
    124                 // 下一次的进水口就有可能是在上边或者在下边了,这取决于用哪一种摆放方式
    125                 dfs(x + 1, y, 2, stack);
    126                 dfs(x - 1, y, 4, stack);
    127             }
    128 
    129             // 进水口在上边的情况
    130             if(front == 2) {
    131 
    132                 // 此时可以有1,4号两种摆放方式
    133                 dfs(x, y + 1, 1, stack);
    134                 dfs(x, y - 1, 3, stack);
    135             }
    136 
    137             // 进水口在右边的情况
    138             if(front == 3) {
    139 
    140                 // 此时可以有1,2号两种摆放方式
    141                 dfs(x - 1, y, 4, stack);
    142                 dfs(x + 1, y, 2, stack);
    143             }
    144 
    145             // 进水口在下边的情况
    146             if(front == 4) {
    147 
    148                 // 此时可以有2,3号两种摆放方式
    149                 dfs(x, y + 1, 1, stack);
    150                 dfs(x, y - 1, 3, stack);
    151             }
    152 
    153         }
    154 
    155         // 尝试完这种方式,如果不行则需要回退重新尝试,取消桶标记,同时栈中记录的点位出栈
    156         book[x][y] = 0;
    157         stack.top --;
    158         return;
    159     }
    160 
    161     public static void main(String[] args) {
    162         // 铺设管道开始的点位,同时第一个进水方先是1
    163         int startx = 0, starty = 0, front = 1;
    164 
    165         // 设定游戏地图边界
    166         m = 5;
    167         n = 4;
    168 
    169         // 初始化游戏地图
    170         map[0][0] = 5;
    171         map[0][1] = 3;
    172         map[0][2] = 5;
    173         map[0][3] = 3;
    174         map[1][0] = 1;
    175         map[1][1] = 5;
    176         map[1][2] = 3;
    177         map[1][3] = 0;
    178         map[2][0] = 2;
    179         map[2][1] = 3;
    180         map[2][2] = 5;
    181         map[2][3] = 1;
    182         map[3][0] = 6;
    183         map[3][1] = 1;
    184         map[3][2] = 1;
    185         map[3][3] = 5;
    186         map[4][0] = 1;
    187         map[4][1] = 5;
    188         map[4][2] = 5;
    189 
    190         map[4][3] = 4;
    191 
    192         // 初始化记录管道铺设路径的栈
    193         PlumberStack stack = new PlumberStack();
    194 
    195         // 开始游戏
    196         dfs(startx, starty, front, stack);
    197 
    198         if(flag == 0) {
    199             System.out.println("Impossible!");
    200         }else {
    201             System.out.println("
    Yes, we did it.");
    202         }
    203     }
    204 }

    参考资料

    1、《啊哈!算法》/ 啊哈磊著. 人民邮电出版社

  • 相关阅读:
    H5定位终极解决方案
    软帝学院教你使用cookie法,查看最近看过的书
    你真的会用Gson吗?Gson使用指南(一)
    Java程序员应当知道的10个面向对象设计原则
    java获取当前月第一天和最后一天,上个月第一天和最后一天
    正则基础教程一些冷门的知识
    爆笑的程序员梗,笑死人不偿命!
    java字符串操作扩充:灵活截取字符串
    如何分析及处理 Flink 反压?
    与君初相识,犹如故人归
  • 原文地址:https://www.cnblogs.com/captainad/p/11065301.html
Copyright © 2011-2022 走看看