Java玩转《啊哈算法》暴力枚举之炸弹人

当一个人身强力壮、洋溢着青春气息的时候,要做到坚强是比较简单和容易的。只有当生活像铁环一样把你紧紧箍住的时候,能做到坚强才是光荣的。

有目录,不迷路

缘起

各位小伙伴们好呀!本人最近看了下《啊哈算法》,写的确实不错。

但稍显遗憾的是,书籍示例代码是c语言,而不是本人常用的Java。

那就弥补遗憾,说干就干,把这本书的示例语言用java写一遍, 顺带附上一些自己的理解!

今天我们来用枚举的思想玩一个炸弹人的游戏!
在这里插入图片描述

来不及买纸质书但又想尽快感受算法魅力的童鞋也甭担心,电子版的下载链接已经放到下方了,可尽情下载。

链接:https://pan.baidu.com/s/1imxiElcCorw2F-HJEnB-PA?pwd=jmgs
提取码:jmgs

代码地址

本文代码已开源:

git clone https://gitee.com/guqueyue/my-blog-demo.git

请切换到gitee分支,

然后查看aHaAlgorithm模块下的src/main/java/com/guqueyue/aHaAlgorithm/chapter_3_Enum即可!

炸弹人

说起炸弹人,大家可能想到的是英雄联盟游戏里面的爆破鬼才 — 吉格斯。

在这里插入图片描述

不过这里说的是小霸王游戏机里面的“炸弹人”游戏,一听就有点年代感了。

在这里插入图片描述

现在有一个特殊的关卡如下。你只有一枚炸弹,但是这枚炸弹威力超强(杀伤距离超长,可以消灭杀伤范围内所有的敌人)。
请问在哪里放置炸弹才可以消灭最多的敌人呢?

在这里插入图片描述
那么,我们要怎么用代码来计算这个问题呢?

首先,我们要将图形化的地图进行模型化处理,我们用 # 来表示墙壁、 G表示敌人,.表示空地,

其中炸弹只能放在空地上,爆炸范围为上、下、左、右四个范围,并且不能穿墙。

char[][] arr  = {
                {'#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#'},
                {'#', 'G', 'G', '.', 'G', 'G', 'G', '#', 'G', 'G', 'G', '.', '#'},
                {'#', '#', '#', '.', '#', 'G', '#', 'G', '#', 'G', '#', 'G', '#'},
                {'#', '.', '.', '.', '.', '.', '.', '.', '#', '.', '.', 'G', '#'},
                {'#', 'G', '#', '.', '#', '#', '#', '.', '#', 'G', '#', 'G', '#'},
                {'#', 'G', 'G', '.', 'G', 'G', 'G', '.', '#', '.', 'G', 'G', '#'},
                {'#', 'G', '#', '.', '#', 'G', '#', '.', '#', '.', '#', '#', '#'},
                {'#', '#', 'G', '.', '.', '.', 'G', '.', '.', '.', '.', '.', '#'},
                {'#', 'G', '#', '.', '#', 'G', '#', '#', '#', '.', '#', 'G', '#'},
                {'#', '.', '.', '.', 'G', '#', 'G', 'G', 'G', '.', 'G', 'G', '#'},
                {'#', 'G', '#', '.', '#', 'G', '#', 'G', '#', '.', '#', 'G', '#'},
                {'#', 'G', 'G', '.', 'G', 'G', 'G', '#', 'G', '.', 'G', 'G', '#'},
                {'#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#'}
};

那么,初始化地图之后,我们要怎么利用枚举的思想来解决这个问题呢?

很简单,暴力就好了。我们直接遍历每个点,计算每个点可以消灭敌人的个数就好了。

如果遇到可以消灭敌人更多数的点,就把它的坐标和可消灭敌人数记录下来。

在这里插入图片描述
至于如何计算每个点可以消灭敌人的个数,写四个循环分别往四个方向计算就好了。例如,求上方的可消灭敌人数:

 int sum = 0, x = i, y = j; // sum: 当前坐标可消灭敌人数,x: 当前横坐标,y: 当前纵坐标
 // 向上找
 while (arr[x][y] != '#' && x >= 0 && x < w) { // 四周一定有墙,所以 坐标是否越界其实不用判断

     if (arr[x][y] == 'G') {
         sum++;
     }

     x--;
 }

因为我们这里是向上,所以用x--,其他方向如下图所示:
在这里插入图片描述

完整代码如下:

package com.guqueyue.aHaAlgorithm.chapter_3_Enum;

/**
 * @Author: guqueyue
 * @Description: 炸弹人
 * @Date: 2024/1/17
 **/
public class BomberMan {
    public static void main(String[] args) {

        char[][] arr  = {
                {'#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#'},
                {'#', 'G', 'G', '.', 'G', 'G', 'G', '#', 'G', 'G', 'G', '.', '#'},
                {'#', '#', '#', '.', '#', 'G', '#', 'G', '#', 'G', '#', 'G', '#'},
                {'#', '.', '.', '.', '.', '.', '.', '.', '#', '.', '.', 'G', '#'},
                {'#', 'G', '#', '.', '#', '#', '#', '.', '#', 'G', '#', 'G', '#'},
                {'#', 'G', 'G', '.', 'G', 'G', 'G', '.', '#', '.', 'G', 'G', '#'},
                {'#', 'G', '#', '.', '#', 'G', '#', '.', '#', '.', '#', '#', '#'},
                {'#', '#', 'G', '.', '.', '.', 'G', '.', '.', '.', '.', '.', '#'},
                {'#', 'G', '#', '.', '#', 'G', '#', '#', '#', '.', '#', 'G', '#'},
                {'#', '.', '.', '.', 'G', '#', 'G', 'G', 'G', '.', 'G', 'G', '#'},
                {'#', 'G', '#', '.', '#', 'G', '#', 'G', '#', '.', '#', 'G', '#'},
                {'#', 'G', 'G', '.', 'G', 'G', 'G', '#', 'G', '.', 'G', 'G', '#'},
                {'#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#'}
        };

        // maxSum: 最大可消灭敌人数,maxX: 最大可消灭敌人数的横坐标,maxY: 最大可消灭敌人数的纵坐标, w: 矩阵宽度,h: 矩阵高度
        int maxSum = 0, maxX = -1, maxY = -1, w = arr.length, h = arr[0].length;
        for (int i = 0; i < w; i++) {
            for (int j = 0; j < h; j++) {

                // 空地才可以防止炸弹
                if(arr[i][j] != '.') {
                    continue;
                }

                int sum = 0, x = i, y = j; // sum: 当前坐标可消灭敌人数,x: 当前横坐标,y: 当前纵坐标
                // 向上找
                while (arr[x][y] != '#' && x >= 0 && x < w) { // 四周一定有墙,所以 坐标是否越界其实不用判断

                    if (arr[x][y] == 'G') {
                        sum++;
                    }

                    x--;
                }

                // 向下找
                x = i;
                y = j;
                while (arr[x][y] != '#' && x >= 0 && x < w) {

                    if (arr[x][y] == 'G') {
                        sum++;
                    }

                    x++;
                }

                // 向左找
                x = i;
                y = j;
                while (arr[x][y] != '#' && y >= 0 && y < h) {

                    if (arr[x][y] == 'G') {
                        sum++;
                    }

                    y--;
                }

                // 向右找
                x = i;
                y = j;
                while (arr[x][y] != '#' && y >= 0 && y < h) {

                    if (arr[x][y] == 'G') {
                        sum++;
                    }

                    y++;
                }


                if (sum > maxSum) {
                    maxX = i;
                    maxY = j;
                    maxSum = sum;
                }
            }
        }

        System.out.printf("在(%d, %d)处放置炸弹,杀伤的敌人最多,为%d人\n", maxX, maxY, maxSum);
    }
}

运行,得:

在这里插入图片描述

问题

上文的代码虽然偏长,但是思路是不是很简单顺畅?

但是正如作者所说,枚举的思想虽然简单粗暴,但是不够智能:
在这里插入图片描述
如,我们像作者一样更改地图:

package com.guqueyue.aHaAlgorithm.chapter_3_Enum;

/**
 * @Author: guqueyue
 * @Description: 炸弹人
 * @Date: 2024/1/17
 **/
public class BomberMan {
    public static void main(String[] args) {

        char[][] arr  = {
                {'#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#'},
                {'#', 'G', 'G', '.', 'G', 'G', 'G', '#', 'G', 'G', 'G', '.', '#'},
                {'#', '#', '#', '.', '#', 'G', '#', 'G', '#', 'G', '#', 'G', '#'},
                {'#', '.', '.', '.', '.', '.', '.', '.', '#', '.', '.', 'G', '#'},
                {'#', 'G', '#', '.', '#', '#', '#', '.', '#', 'G', '#', 'G', '#'},
                {'#', 'G', 'G', '.', 'G', 'G', 'G', '.', '#', '.', 'G', 'G', '#'},
                {'#', 'G', '#', '.', '#', 'G', '#', '.', '#', '.', '#', '#', '#'},
                {'#', '#', 'G', '.', '.', '.', 'G', '.', '.', '.', '.', '.', '#'},
                {'#', 'G', '#', '.', '#', 'G', '#', '#', '#', '.', '#', 'G', '#'},
                {'#', '.', '.', '.', 'G', '#', 'G', 'G', 'G', '.', 'G', 'G', '#'},
                {'#', 'G', '#', '.', '#', 'G', '#', 'G', '#', '.', '#', 'G', '#'},
                {'#', 'G', 'G', '.', 'G', 'G', 'G', '#', 'G', '.', 'G', 'G', '#'},
                {'#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#'}
        };

        arr = new char[][]{
                {'#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#'},
                {'#', 'G', 'G', '.', 'G', 'G', 'G', '#', 'G', 'G', 'G', '.', '#'},
                {'#', '#', '#', '.', '#', 'G', '#', 'G', '#', 'G', '#', 'G', '#'},
                {'#', '.', '.', '.', '.', '.', '.', '.', '#', '.', '.', 'G', '#'},
                {'#', 'G', '#', '.', '#', '#', '#', '.', '#', 'G', '#', 'G', '#'},
                {'#', 'G', 'G', '.', 'G', 'G', 'G', '.', '#', '.', 'G', 'G', '#'},
                {'#', 'G', '#', '.', '#', 'G', '#', '.', '#', '.', '#', '.', '#'},
                {'#', '#', 'G', '.', '.', '.', 'G', '.', '.', '.', '.', '.', '#'},
                {'#', 'G', '#', '.', '#', 'G', '#', '#', '#', '.', '#', 'G', '#'},
                {'#', '.', '.', '.', 'G', '#', 'G', 'G', 'G', '.', 'G', 'G', '#'},
                {'#', 'G', '#', '.', '#', 'G', '#', 'G', '#', '.', '#', 'G', '#'},
                {'#', 'G', 'G', '.', 'G', 'G', 'G', '#', 'G', '.', 'G', 'G', '#'},
                {'#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#'}
        };

        // maxSum: 最大可消灭敌人数,maxX: 最大可消灭敌人数的横坐标,maxY: 最大可消灭敌人数的纵坐标, w: 矩阵宽度,h: 矩阵高度
        int maxSum = 0, maxX = -1, maxY = -1, w = arr.length, h = arr[0].length;
        for (int i = 0; i < w; i++) {
            for (int j = 0; j < h; j++) {

                // 空地才可以防止炸弹
                if(arr[i][j] != '.') {
                    continue;
                }

                int sum = 0, x = i, y = j; // sum: 当前坐标可消灭敌人数,x: 当前横坐标,y: 当前纵坐标
                // 向上找
                while (arr[x][y] != '#' && x >= 0 && x < w) { // 四周一定有墙,所以 坐标是否越界其实不用判断

                    if (arr[x][y] == 'G') {
                        sum++;
                    }

                    x--;
                }

                // 向下找
                x = i;
                y = j;
                while (arr[x][y] != '#' && x >= 0 && x < w) {

                    if (arr[x][y] == 'G') {
                        sum++;
                    }

                    x++;
                }

                // 向左找
                x = i;
                y = j;
                while (arr[x][y] != '#' && y >= 0 && y < h) {

                    if (arr[x][y] == 'G') {
                        sum++;
                    }

                    y--;
                }

                // 向右找
                x = i;
                y = j;
                while (arr[x][y] != '#' && y >= 0 && y < h) {

                    if (arr[x][y] == 'G') {
                        sum++;
                    }

                    y++;
                }


                if (sum > maxSum) {
                    maxX = i;
                    maxY = j;
                    maxSum = sum;
                }
            }
        }

        System.out.printf("在(%d, %d)处放置炸弹,杀伤的敌人最多,为%d人\n", maxX, maxY, maxSum);
    }
}

在这里插入图片描述
运行,得:

在这里插入图片描述

但是,很明显 (1, 11) 小人根本走不进去:

在这里插入图片描述
所以,这个答案是错误的,我们使用枚举的思想根本无法判断哪片空地小人是否可以到达。

那,怎么办呢?不要着急,我们在后期的博客中讲《万能的搜索》时,再来解决这个问题!

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值