蓝桥杯第642题——跳蚱蜢

题目描述

如下图所示: 有 9 只盘子,排成 1 个圆圈。 其中 8 只盘子内装着 8 只蚱蜢,有一个是空盘。 我们把这些蚱蜢顺时针编号为 1 ~ 8。

图片描述

每只蚱蜢都可以跳到相邻的空盘中, 也可以再用点力,越过一个相邻的蚱蜢跳到空盘中。

请你计算一下,如果要使得蚱蜢们的队形改为按照逆时针排列, 并且保持空盘的位置不变(也就是 1−8 换位,2−7换位,...),至少要经过多少次跳跃?

解题思路

这道题是典型的BFS相关例题,可以练习BFS判重,以及双向广搜的具体方式。

我们从蚱蜢跳跃去考虑并不方便,因为我们每次都有很多只蚱蜢可以进行跳跃,因此我们可以转换思维,考虑到只有一个空盘子,所以第一次跳跃的情况是必定有一只蚱蜢跳到空盘子里;而下一次操作也是同样的情况。

所以我们考虑有哪些蚱蜢可以跳到空盘子上,进一步思考,就是空盘子可以与哪些蚱蜢交换位置,很明显,如图所示,0号空盘可以与2、1、8、7号蚱蜢交换位置,这就为我们提供了搜索路径的灵感;同时,我们是要找到最少的跳跃次数,也就是最短路径,因此我们考虑广度优先搜索。

因为这题不像迷宫,有一个明显的特点,也就是0和2交换的结果——可以由0和1交换,0和2交换,1再和2交换获得。那么这就会导致很多重复,所以后来变换的结果如果与前面已经获取过的结果有重叠,就应当剪枝不再计算,这里我们考虑使用HashSet结构和HashMap结构进行去重。

单点BFS

我们使用一个ArrayDeque构成的队列实现BFS,队列中每个节点的内容是当前的字符串s和得到它所走过的步数step,另外使用一个HashSet来记录搜索过的字符串,很明显先进来的字符串所带来的step数值是最小的,判重过后只需要忽略后续来的相同字符串即可。

以下是常规bfs判重的解题代码:

import java.io.*;
import java.util.*;

public class Main {
    static String S1 = "012345678";
    static String S2 = "087654321";
    static int num = 0;     // 计算队列的操作次数,用于与双向广搜对比
    public static void main(String[] args) throws IOException {
         Set<String> set = new HashSet<>();
         ArrayDeque<Pair> qu = new ArrayDeque<>();
         qu.addLast(new Pair(S1, 0));
         set.add(S1);
         while (!qu.isEmpty()) {
             Pair temp = qu.removeFirst();
             if (temp.s.equals(S2)) {
                 System.out.println(temp.step);
                 System.out.println(num);
                 break;
             }
             // 使用index记录当前字符串中0所在的位置
             int index = 0;
             for (int i = 0; i < 9; i++) {
                 if (temp.s.charAt(i) == '0') {
                     index = i;
                     break;
                 }
             }
             for (int i = index - 2; i <= index + 2; i++) {
                 num++;     // num的每次自增意味着计算的执行次数增加
                 // 使用取余技巧获取将要进行交换的下标
                 int k = (i + 9) % 9;
                 // 5次循环中忽略实际没有交换的情况
                 if (k == index) {
                     continue;
                 }
                 char[] ss = temp.s.toCharArray();
                 char t = ss[k]; ss[k] = ss[index]; ss[index] = t;
                 if (set.add(String.copyValueOf(ss))) {
                     qu.addLast(new Pair(String.copyValueOf(ss), temp.step + 1));
                 }
             }
         }
    }
}

class Pair {
    String s;
    int step;

    public Pair(String s, int step) {
        this.s = s;
        this.step = step;
    }
}

 结果输出中,第一个数值是题目所要求的答案,第二个值代表着该算法的运行次数。

20
1814315
第一种思路的局限性

在考虑单点bfs找最短路径的情况,我们可以想象原点是一颗由上至下的4叉树,那么题目的答案20,意味着我们要向下分出20层才能找到答案,树会不断往底部扩展,越是向下的层数,所需要计算的节点就越是多,如果我们没有去重,想象一个完整的20层4叉树,它是非常巨大的。

那么我们可以考虑另外一种情况,现在我们明确的知道目标结果的字符串,那么我们可以不可以根据最终状态再反向生成一颗树呢?

如果从上至下以及从下至上同时开始广搜,那么我们所需要生成的四叉树就从一个20层的树变成了两个10层的树,同学们可以自行想象,这对时间的节省又是相当巨大的。

双向广搜

在上述单点BFS的基础上,我们进行升级为双向广搜,每次对队列的一次BFS操作都根据队列长度较短的进行选择执行,第一次遇到相同字符串的时候,两个层数加起来就是我们的答案。

我们需要将第一种方式的一个Set修改为两个独立的HashMap,记录字符串出现的同时,也要对应记录其出现的层数,方便我们对答案进行输出。

import java.io.*;
import java.util.*;

public class Main {
    static String S1 = "012345678";
    static String S2 = "087654321";
    static int times = 0;
    static int ans = 0;
    public static void main(String[] args) throws IOException {
        HashMap<String, Integer> map1 = new HashMap<>();
        HashMap<String, Integer> map2 = new HashMap<>();
        ArrayDeque<Pair> qu1 = new ArrayDeque<>();
        ArrayDeque<Pair> qu2 = new ArrayDeque<>();
        qu1.addLast(new Pair(S1, 0));
        map1.put(S1, 0);
        qu2.addLast(new Pair(S2, 0));
        map2.put(S2, 0);

        while (!qu1.isEmpty() && !qu2.isEmpty()) {
            if (qu1.size() < qu2.size()) {
                extend(qu1, map1, map2);
            } else {
                extend(qu2, map2, map1);
            }
            if (ans != 0) {
                System.out.println(ans);
                System.out.println(times);
                break;
            }
        }
    }
    // 注意该方法中的qu是当前操作的队列
    // map对应的是qu的HashMap
    // 而map1是另外一个队列的HashMap
    public static void extend(ArrayDeque<Pair> qu, HashMap<String, Integer> map, HashMap<String, Integer> map1) {
        Pair temp = qu.removeFirst();
        if (map1.containsKey(temp.s)) {
            ans = temp.step + map1.get(temp.s);
            return;
        }
        int index = -1;
        for (int i = 0; i < temp.s.length(); i++) {
            if (temp.s.charAt(i) == '0') {
                index = i;
                break;
            }
        }
        for (int i = index - 2; i <= index + 2; i++) {
            times++;
            int k = (i + temp.s.length()) % temp.s.length();
            if (k == index) {
                continue;
            }
            char[] arr = temp.s.toCharArray();
            char ch = arr[index]; arr[index] = arr[k]; arr[k] = ch;
            String news = String.copyValueOf(arr);
            if (!map.containsKey(news)) {
                map.put(news, temp.step + 1);
                qu.addLast(new Pair(news, temp.step + 1));
            }
        }
    }
}

class Pair {
    String s;
    int step;

    public Pair(String s, int step) {
        this.s = s;
        this.step = step;
    }
}
20
133510

通过两种方法的输出对比我们可以看出,对队列的操作次数由180万降低到了13万,这也是双向广搜的厉害之处。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/466926.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

手撕算法-二叉树的镜像

题目描述 操作给定的二叉树&#xff0c;将其变换为源二叉树的镜像。数据范围&#xff1a;二叉树的节点数 0≤_n_≤1000 &#xff0c; 二叉树每个节点的值 0≤_val_≤1000要求&#xff1a; 空间复杂度 O(n) 。本题也有原地操作&#xff0c;即空间复杂度 O(1) 的解法&#xff0c…

双线性插值

1.线性插值 即是提供一次线性已知点去拟合曲线再求得插值点。 2.原理 两次不同方向的插值&#xff0c;先对已知的四个点的值通过两次一次线性插值获取两个点&#xff0c;再通过刚刚获得的两个点的值再进行一次线性插值&#xff0c;从而根据已知的四个点的值获得一个未知的点…

Layui实现删除及修改后停留在当前页

1、功能概述&#xff1f; 我们在使用layui框架的table显示数据的时候&#xff0c;会经常的使用分页技术&#xff0c;这个我们期望能够期望修改数据能停留在当前页&#xff0c;或者删除数据的时候也能够停留在当前页&#xff0c;这样的用户体验会更好一些&#xff0c;但往往事与…

python 基础知识点(蓝桥杯python科目个人复习计划65)

今日复习内容&#xff1a;做题 例题1&#xff1a;遥远的雪国列车 问题描述&#xff1a; 小蓝和小红今天在房间里一起看完了“雪国列车”这部电影&#xff0c;看完之后他们感触颇深&#xff0c;同时他们想到了这样一道题目&#xff1a; 现在有一个数轴&#xff0c;长度为N&a…

thinkphp 微信商户付款到微信小程序用户零钱(v2密钥版)

这几天做项目有一个需求,小程序用户提交记录后,商家后台审核通过自动转账到用户的微信零钱中. 今天分享下如何实现自动打款: 一种是用v2密钥的接口:企业付款到零钱, 一种需要用v3密钥的接口:微信商户转账到零钱 php后端代码 v2企业付款到零钱 /*** 审核通过红包打款* @aut…

Java开发者的新宠:探索轻量级且功能强大的Magic-API

Java开发者的新宠&#xff1a;探索轻量级且功能强大的Magic-API 一、Magic-API简介二、Magic-API的核心特性三、结语 大家好&#xff0c;这里是程序猿代码之路&#xff0c;在当今的软件开发领域&#xff0c;快速迭代和高效交付是每个项目追求的目标。对于Java开发者来说&#x…

Cloudways搭建WordPress外贸独立站完整教程

现在做个网站不比从前了&#xff0c;搭建网站非常的简单&#xff0c;主要是由于开源的CMS建站系统的崛起&#xff0c;就算不懂编程写代码的人也能搭建一个自己的网站&#xff0c;这些CMS系统提供了丰富的主题模板和插件&#xff0c;使用户可以通过简单的拖放和配置操作来建立自…

二、SQL基础学习(函数、约束、事务)

目录 1、函数1.1、字符串函数1.2、数值函数1.3、日期函数1.4 、流程函数 2、约束2.1、外键约束2.2、删除/更新行为 3、事务3.1、事务的四大特性3.2、并发事务问题3.2、事务的隔离级别 1、函数 1.1、字符串函数 # concat select concat(Hello, MySql);# lower select lower(He…

Unity InputField实现框自适应内容简便方法

要实现InputField框自适应输入内容&#xff0c;除了通过代码进行处理&#xff0c;还可以是使用以下简便的方法。 1、创建InputField组件&#xff1a;右键->UI->Input Field -TextMeshPro。 2、把Input Field Settings中的Line Type设置为Multi Line Newline模式&#x…

探索NFT数字藏品交易平台:发现新的数字艺术世界

探索NFT数字藏品交易平台&#xff1a;发现新的数字艺术世界 随着数字化时代的来临&#xff0c;NFT&#xff08;非同质化代币&#xff09;技术正在改变艺术市场的格局&#xff0c;使得数字艺术品成为热门投资对象。而要进入这个令人兴奋的领域&#xff0c;您需要了解一些主要的…

区间和(图论)

小明与小红在玩一个猜谜游戏。小红有一个长度为N的下标从1开始的数组A。起初时&#xff0c;小明并不知道数组里的任何数。但是小红会告诉小明Q个关于数组A的信息&#xff0c;每个信息包括三个数字L、R、W表示&#xff1a;A[L] A[L 1] ... A[R] W 现在小红要小明用这Q组信…

hadoop分布式环境搭建

准备三台centos虚拟机 。&#xff08;master&#xff0c;slave1&#xff0c;slave2&#xff09; (hadoop、jdk文件链接&#xff1a;https://pan.baidu.com/s/1wal1CSF1oO2h4dkSbceODg 提取码&#xff1a;4zra) 前四步可参考hadoop伪分布式环境搭建详解-CSDN博客 1.修改主机名…

免登录积分商城系统 动力商城 兑换商城源码

内容目录 一、详细介绍二、效果展示1.部分代码2.效果图展示 三、学习资料下载 一、详细介绍 免登录积分商城源码/动力商城/兑换商城系统 之前互站买来的&#xff0c;看着还是很不错的&#xff0c;不需要注册登录的商城&#xff0c;东西完整。UI也挺漂亮&#xff0c;这相当于是…

全球造爆款,海尔智家凭什么?

据说&#xff0c;广东人是地球上最像三体人的群体&#xff0c;因为需要时刻小心脱水和浸泡的时机。 这是因为广东人每年春天都会经历的现实噩梦“回南天”。墙壁淌水、地板湿滑、衣服干不了……浸泡在回南天里的广东人&#xff0c;喜提最新地狱笑话&#xff1a;“广东人有望最…

.rmallox勒索病毒解密方法|勒索病毒解决|勒索病毒恢复|数据库修复

导言&#xff1a; 近年来&#xff0c;勒索病毒的威胁日益增加&#xff0c;其中一种名为.rmallox的勒索病毒备受关注。这种病毒通过加密文件并勒索赎金来威胁受害者。本文将介绍.rmallox勒索病毒的特点&#xff0c;以及如何恢复被其加密的数据文件&#xff0c;并提供预防措施&a…

【kaggle竞赛】从手写图像数据集中正确识别数字

1. 题目&#xff1a; 在本次比赛中&#xff0c;您的目标是从数以万计的手写图像数据集中正确识别数字。 1.1. Goal 目标✨ 本次比赛的目标是拍摄手写个位数的图像&#xff0c;并确定该数字是什么。 对于测试集中的每个标签&#xff0c;您都应该预测正确的标签。 本次比赛的…

《我的AUTOSAR之路》ECUM(二) 唤醒处理

ECUM唤醒 1 EcuM 唤醒源2 EcuM 唤醒源配置3 Can 通道唤醒源调用解析1 EcuM 唤醒源 AUTOSAR 唤醒过程包含的步骤 检查唤醒源和上报唤醒时间唤醒源保护唤醒过程是独立于 EcuM 休眠阶段的,但是唤醒时间可以用于休眠阶段 在整个 Ecu 所有阶段,唤醒事件都可以存在唤醒不单单指 Ecu …

【Nutx3】middleware目录介绍

简言 记录下nuxt3middleware目录的使用方法。 middleware middleware是存放路由中间件的文件目录。 路由中间件有三种&#xff1a; 匿名&#xff08;或内联&#xff09;路由中间件直接在页面中定义。已命名的路由中间件&#xff0c;放在 middleware/ 中&#xff0c;页面使用…

4.1_4 文件的物理结构

文章目录 4.1_4 文件的物理结构&#xff08;一&#xff09;文件块、磁盘块&#xff08;二&#xff09;文件分配方式——连续分配&#xff08;三&#xff09;文件分配方式——链接分配&#xff08;1&#xff09;链接分配——隐式链接&#xff08;2&#xff09;链接分配——显式链…

慢sql优化

1.避免使用select *&#xff0c;而是明确列出需要的列&#xff0c; 2.小表驱动大表&#xff0c;in适用于左边大表&#xff0c;右边小表。 exists适用于左边小表&#xff0c;右边大表。 3.批量操作&#xff1a;如果每次插入数据库数据&#xff0c;都要连接一次数据库&#xf…