当前位置: 首页 > article >正文

算法系列之广度优先搜索解决妖怪和尚过河问题

_20250308_234624.png

在算法学习中,广度优先搜索(BFS)是一种常用的图搜索算法,适用于解决最短路径问题、状态转换问题等。本文将介绍如何利用广度优先搜索解决经典的“妖怪和尚过河问题”。

问题描述

有三个妖怪和三个和尚需要过河。他们只有一条小船,且船最多只能载两人。在任何时候,无论是岸边还是船上,如果妖怪的数量多于和尚,和尚就会被吃掉。如何安排过河顺序,才能让所有妖怪和和尚安全过河?

_20250308_232541.png

问题分析

首先,我们需要将建立状态和动作的数学模型,我们要明确问题的状态表示。我们用五个属性来表示状态,

//左岸和尚数量
int leftMonk;
//左岸妖怪数量
int leftMonster;
//右岸和尚数量
int rightMonk;
//右岸妖怪数量
int rightMonster;
//-1代表左岸,1代表右岸
int boatLocation;

结合船移动的方向,一共右10中过河动作可供选择,分别是

* 1、一个妖怪从左岸到右岸
* 2、一个和尚从左岸到右岸
* 3、两个妖怪从左岸到右岸
* 4、两个和尚从左岸到右岸
* 5、一个和尚一个妖怪从左岸到右岸
* 6、一个妖怪从右岸到左岸
* 7、一个和尚从右岸到左岸
* 8、两个妖怪从右岸到左岸
* 9、两个和尚从右岸到左岸
* 10、一个和尚一个妖怪从右岸到左岸

我们的目标是从初始状态 (3, 3, 0, 0, -1) 通过一系列合法的移动,达到目标状态 (0, 0, 3, 3, 1)。

Java使用BFS解决问题

BFS 是一种逐层扩展的搜索算法,适用于寻找最短路径。我们可以将每个状态看作图中的一个节点,合法的移动就是节点之间的边。通过 BFS,我们可以找到从初始状态到目标状态的最短路径。

算法步骤

  1. 初始化:将初始状态 (3, 3, 0, 0, -1) 加入队列,并标记为已访问。

  2. 循环处理队列:

  • 从队列中取出一个状态。

  • 生成所有可能的下一步状态。

  • 检查这些状态是否合法(即不违反妖怪和和尚的数量限制)。

  • 如果某个状态是目标状态,则打印路径并返回。

  • 否则,将合法的未访问状态加入队列,并标记为已访问。

  1. 重复步骤2,直到队列为空或找到目标状态。

代码实现如下:

/*** 妖怪与和尚过河问题* 描述:* 有三个和尚和三个妖怪在河的左岸,他们需要过河到右岸。** 只有一条小船,最多可以承载两个人。** 在任何时候,无论是左岸还是右岸,如果和尚的数量少于妖怪的数量,和尚就会被妖怪吃掉。*/
public class crossRiver {/*** 状态类(数据模型)*/public static class State{//左岸和尚数量int leftMonk;//左岸妖怪数量int leftMonster;//右岸和尚数量int rightMonk;//右岸妖怪数量int rightMonster;//-1代表左岸,1代表右岸int boatLocation;//前一状态,用于回溯打印路径State preState;public State(int leftMonk, int leftMonster, int rightMonk, int rightMonster, int boatLocation, State preState) {this.leftMonk = leftMonk;this.leftMonster = leftMonster;this.rightMonk = rightMonk;this.rightMonster = rightMonster;this.boatLocation = boatLocation;this.preState = preState;}@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;State state = (State) o;return leftMonk == state.leftMonk && leftMonster == state.leftMonster && rightMonk == state.rightMonk && rightMonster == state.rightMonster && boatLocation == state.boatLocation ;}@Overridepublic int hashCode() {return Objects.hash(leftMonk, leftMonster, rightMonk, rightMonster, boatLocation);}}/*** 动作类,记录妖怪与和尚过河的动作*/@Datapublic static class Action{int monkNum; //船上和尚数量int monsterNum;//船上妖怪数量int boatLocation;//船移动后位置public Action(int monkNum, int monsterNum, int boatLocation) {this.monkNum = monkNum;this.boatLocation = boatLocation;this.monsterNum = monsterNum;}}/*** 列举过河动作的可供选择* 共有十种情况* 1、一个妖怪从左岸到右岸* 2、一个和尚从左岸到右岸* 3、两个妖怪从左岸到右岸* 4、两个和尚从左岸到右岸* 5、一个和尚一个妖怪从左岸到右岸* 6、一个妖怪从右岸到左岸* 7、一个和尚从右岸到左岸* 8、两个妖怪从右岸到左岸* 9、两个和尚从右岸到左岸* 10、一个和尚一个妖怪从右岸到左岸**/public static List<Action> getActions(){List<Action> actions = new ArrayList<>();//一个妖怪从左岸到右岸actions.add(new Action(0,1,1));//两个妖怪从左岸到右岸actions.add(new Action(0,2,1));//一个和尚从左岸到右岸actions.add(new Action(1,0,1));//两个和尚从左岸到右岸actions.add(new Action(2,0,1));//一个和尚一个妖怪从左岸到右岸actions.add(new Action(1,1,1));//一个妖怪从右岸到左岸actions.add(new Action(0,1,-1));//两个妖怪从右岸到左岸actions.add(new Action(0,2,-1));//一个和尚从右岸到左岸actions.add(new Action(1,0,-1));//两个和尚从右岸到左岸actions.add(new Action(2,0,-1));//一个和尚一个妖怪从右岸到左岸actions.add(new Action(1,1,-1));return actions;}/*** 初始状态*/public static State initState(){State state = new State(3,3,0,0,-1,null);return state;}/*** 生成所有可能的下一个状态*/public static List<State> generateNextStates(State state){List<State> nextStates = new ArrayList<>();State nextState;for (Action action : getActions()) {if(state.boatLocation != action.boatLocation){nextState = new State(state.leftMonk - action.monkNum*action.boatLocation, state.leftMonster - action.monsterNum*action.boatLocation,state.rightMonk + action.monkNum*action.boatLocation, state.rightMonster + action.monsterNum*action.boatLocation,action.boatLocation, state);//有效则添加if(checkState(nextState)){nextStates.add(nextState);}}}return nextStates;}/*** 检查状态是否有效,(无论是左岸还是右岸,和尚数量大于妖怪数量则有效)*/public static boolean checkState(State state) {//任何一岸的和尚数量不能少于妖怪数量,除非和尚数量为0。if(state.leftMonk < 0 || state.leftMonster < 0 || state.rightMonk < 0 || state.rightMonster < 0){return false;}//不管是左岸还是右岸,和尚数量大于妖怪数量或者和尚全部在河对岸则有效,船也只能从对岸来回开return (state.leftMonk == 0 || state.leftMonk >= state.leftMonster)&& (state.rightMonk == 0 || state.rightMonk >= state.rightMonster) && state.boatLocation !=state.preState.boatLocation;}/*** 判断是否成功*/public static boolean isSuccess(State state){return state.leftMonk == 0 && state.leftMonster == 0;}/*** 广度优先搜索方式解决渡河问题*/public static void crossRiver(State initState){//访问的节点队列Queue<State> queue = new LinkedList<>();queue.add(initState);//访问过的状态集合Set<State> visited = new HashSet<>();visited.add(initState);List<State> nextStates;while (!queue.isEmpty()){State currentState = queue.poll();//成功打印路径并退出if(isSuccess(currentState)) {printPath(currentState);return;}else {nextStates = generateNextStates(currentState);for(State nextState : nextStates){//剪枝判断if(!visited.contains(nextState)){//添加到队列中queue.add(nextState);visited.add(nextState);}}}}}/*** 递归打印路径*/public static void  printPath(State state){if(state.preState == null){return;}printPath(state.preState);System.out.println("从"+(state.preState.boatLocation==-1?"左":"右")+"岸到"+(state.boatLocation==-1?"左":"右")+"岸");System.out.println("和尚:"+state.leftMonk+" "+state.rightMonk);System.out.println("妖怪:"+state.leftMonster+" "+state.rightMonster);}public static void main(String[] args) {State initState = initState();crossRiver(initState);}
}

执行结果如下:

从左岸到右岸
和尚:3 0
妖怪:1 2
从右岸到左岸
和尚:3 0
妖怪:2 1
从左岸到右岸
和尚:3 0
妖怪:0 3
从右岸到左岸
和尚:3 0
妖怪:1 2
从左岸到右岸
和尚:1 2
妖怪:1 2
从右岸到左岸
和尚:2 1
妖怪:2 1
从左岸到右岸
和尚:0 3
妖怪:2 1
从右岸到左岸
和尚:0 3
妖怪:3 0
从左岸到右岸
和尚:0 3
妖怪:1 2
从右岸到左岸
和尚:0 3
妖怪:2 1
从左岸到右岸
和尚:0 3
妖怪:0 3

总结

通过广度优先搜索算法,我们成功解决了妖怪和尚过河问题。BFS 的优势在于它能够找到最短路径,适用于状态空间较小的问题。对于更复杂的问题,可以考虑使用其他搜索算法,如深度优先搜索(DFS)算法。我们下篇文章使用DFS求解所有可能的路径。

相关文章:

算法系列之广度优先搜索解决妖怪和尚过河问题

在算法学习中&#xff0c;广度优先搜索&#xff08;BFS&#xff09;是一种常用的图搜索算法&#xff0c;适用于解决最短路径问题、状态转换问题等。本文将介绍如何利用广度优先搜索解决经典的“妖怪和尚过河问题”。 问题描述 有三个妖怪和三个和尚需要过河。他们只有一条小船…...

详解常用集合和映射中的线程安全问题

1. 前言 在 Java 中&#xff0c;集合和映射是常用的数据结构&#xff0c;它们分为线程安全和线程不安全两类。我们常用的集合包括&#xff1a;ArrayList、HashSet、CopyOnWriteArrayList、CopyOnWriteArraySet。常用的映射包括&#xff1a;HashMap、ConcurrentHashMap、Hashta…...

计算机毕业设计SpringBoot+Vue.js车辆管理系统(源码+文档+PPT+讲解)

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…...

【js逆向】iwencai国内某金融网站实战

地址&#xff1a;aHR0cHM6Ly93d3cuaXdlbmNhaS5jb20vdW5pZmllZHdhcC9ob21lL2luZGV4 在搜索框中随便输入关键词 查看请求标头&#xff0c;请求头中有一个特殊的 Hexin-V,它是加密过的&#xff1b;响应数据包中全是明文。搞清楚Hexin-V的值是怎么生成的&#xff0c;这个值和cooki…...

安卓设备root检测与隐藏手段

安卓设备root检测与隐藏手段 引言 安卓设备的root权限为用户提供了深度的系统控制能力&#xff0c;但也可能带来安全风险。因此&#xff0c;许多应用&#xff08;如银行软件、游戏和流媒体平台&#xff09;会主动检测设备是否被root&#xff0c;并限制其功能。这种对抗催生了ro…...

【音视频 | AAC】AAC编码库faac介绍、使用步骤、例子代码

&#x1f601;博客主页&#x1f601;&#xff1a;&#x1f680;https://blog.csdn.net/wkd_007&#x1f680; &#x1f911;博客内容&#x1f911;&#xff1a;&#x1f36d;嵌入式开发、Linux、C语言、C、数据结构、音视频&#x1f36d; &#x1f923;本文内容&#x1f923;&a…...

Unity摄像机跟随物体

功能描述 实现摄像机跟随物体&#xff0c;并使物体始终保持在画面中心位置。 实现步骤 创建脚本&#xff1a;在Unity中创建一个新的C#脚本&#xff0c;命名为CameraFollow。 代码如下&#xff1a; using UnityEngine;public class CameraFollow : MonoBehaviour {public Tran…...

dp_走方格(包含dfs分析,记忆化搜索)

类似题目解析&#xff1a;dp_最长上升子序列&#xff08;包含dfs分析&#xff0c;记忆化搜索&#xff09;-CSDN博客 题目链接&#xff1a;2067. 走方格 - AcWing题库 题目图片&#xff1a; 分析题目&#xff08;dfs&#xff09; 这个题目说有一个行为n行&#xff0c;列为m列…...

软考 中级软件设计师 考点笔记总结 day01

文章目录 软考1.0上午考点下午考点 软考1.11、数值及其转换2、计算机内数据表示2.1、定点数 - 浮点数2.2、奇偶校验 和 循环冗余校验 (了解)2.3、海明码 &#xff08;掌握&#xff09;2.4、机器数 软考1.0 上午考点 软件工程基础知识&#xff1a; 开发模型、设计原则、测试方…...

如何用Kimi生成PPT?秒出PPT更高效!

做PPT是不是总是让你头疼&#xff1f;&#x1f629; 快速制作出专业的PPT&#xff0c;今天我们要推荐两款超级好用的AI工具——Kimi 和 秒出PPT&#xff01;我们来看看哪一款更适合你吧&#xff01;&#x1f680; &#x1f947; Kimi&#xff1a;让PPT制作更轻松 Kimi的生成效…...

K8S学习之基础十八:k8s的灰度发布和金丝雀部署

灰度发布 逐步扩大新版本的发布范围&#xff0c;从少量用户逐步扩展到全体用户。 特点是分阶段发布、持续监控、逐步扩展 适合需要逐步验证和降低风险的更新 金丝雀部署 将新版本先部署到一小部分用户或服务器&#xff0c;观察其表现&#xff0c;再决定是否全面推广。 特点&…...

Java 深度复制对象:从基础到实战

目录 一、深度复制的概念二、实现深度复制的方法1. 使用序列化2. 手动实现深度复制 三、总结 在 Java 编程中&#xff0c;对象的复制是一个常见的需求。然而&#xff0c;简单的复制操作&#xff08;如直接赋值&#xff09;只会复制对象的引用&#xff0c;而不是创建一个新的对象…...

【前端】webstorm创建一个导航页面:HTML、CSS 和 JavaScript 的结合

文章目录 前言一、项目结构二、HTML 结构三、CSS 样式四、JavaScript 功能五、现代化风格优化htmlcssjavascript运行效果 总结 前言 在现代网页开发中&#xff0c;一个良好的导航栏是提升用户体验的重要组成部分。在这篇文章中&#xff0c;我将向您展示如何创建一个简单而完整…...

AI编程: 一个案例对比CPU和GPU在深度学习方面的性能差异

背景 字节跳动正式发布中国首个AI原生集成开发环境工具&#xff08;AI IDE&#xff09;——AI编程工具Trae国内版。 该工具模型搭载doubao-1.5-pro&#xff0c;支持切换满血版DeepSeek R1&V3&#xff0c; 可以帮助各阶段开发者与AI流畅协作&#xff0c;更快、更高质量地完…...

第11章 web应用程序安全(网络安全防御实战--蓝军武器库)

网络安全防御实战--蓝军武器库是2020年出版的&#xff0c;已经过去3年时间了&#xff0c;最近利用闲暇时间&#xff0c;抓紧吸收&#xff0c;总的来说&#xff0c;第11章开始学习利用web应用程序安全&#xff0c;主要讲信息收集、dns以及burpsuite&#xff0c;现在的资产测绘也…...

MySQL复习笔记

MySQL复习笔记 1.MySQL 1.1什么是数据库 数据库(DB, DataBase) 概念&#xff1a;数据仓库&#xff0c;软件&#xff0c;安装在操作系统&#xff08;window、linux、mac…&#xff09;之上 作用&#xff1a;存储数据&#xff0c;管理数据 1.2 数据库分类 关系型数据库&#…...

GitHub上传项目

总结&#xff08;有基础的话直接执行这几步&#xff0c;就不需要再往下看了&#xff09;&#xff1a; git init 修改git的config文件&#xff1a;添加:[user]:name你的github用户名 email你注册github的用户名 git branch -m master main git remote add origin 你的URL gi…...

自我训练模型:通往未来的必经之路?

摘要 在探讨是否唯有通过自我训练模型才能掌握未来的问题时&#xff0c;文章强调了底层技术的重要性。当前&#xff0c;许多人倾向于关注应用层的便捷性&#xff0c;却忽视了支撑这一切的根本——底层技术。将模型简单视为产品是一种短视行为&#xff0c;长远来看&#xff0c;理…...

qt 操作多个sqlite文件

qt 操作多个sqlite文件 Chapter1 qt 操作多个sqlite文件1. 引入必要的头文件2. 创建并连接多个SQLite数据库3. 代码说明4. 注意事项 Chapter2 qt 多线程操作sqlite多文件1. 引入必要的头文件2. 创建数据库操作的工作线程类3. 在主线程中创建并启动多个工作线程4. 代码说明5. 运…...

【每日学点HarmonyOS Next知识】多继承、swiper容器、事件传递、滚动安全区域、提前加载网络图片

1、HarmonyOS ArkTS如何让一个类可以具备其他多个类的能力&#xff1f; ArkTS如何让一个类可以具备其他多个类的能力&#xff0c;类似于多继承。 接口支持多继承。类不支持&#xff0c;其只支持单继承。 &#xff08;报错&#xff1a;Classes can only extend a single class…...

DIY Tomcat:手写一个简易Servlet容器

在Java Web开发领域&#xff0c;Tomcat堪称经典&#xff0c;它作为Servlet容器&#xff0c;承载着无数Web应用的运行。今天&#xff0c;我将带大家一同探索如何手写一个简易的Tomcat&#xff0c;深入理解其底层原理。 一、背景知识 在开始之前&#xff0c;我们需要对几个关键…...

如何在Ubuntu上直接编译Apache Doris

以下是在 Ubuntu 22.04 上直接编译 Apache Doris 的完整流程&#xff0c;综合多个版本和环境的最佳实践&#xff1a; 注意&#xff1a;Ubuntu的数据盘VMware默认是20G&#xff0c;编译不够用&#xff0c;给到50G以上吧 一、环境准备 1. 安装系统依赖 # 基础构建工具链 apt i…...

基于ssm的物资进销存(全套)

现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本货物进销管理系统就是在这样的大环境下诞生&#xff0c;其可以帮助管理者在短时间内处理完毕庞大的数据信息&#…...

【CVPR2025】 EVSSM:用状态空间模型高效去模糊

Efficient Visual State Space Model for Image Deblurring 论文信息 题目&#xff1a; Efficient Visual State Space Model for Image Deblurring 用于图像去模糊的高效视觉状态空间模型 源码&#xff1a;https://github.com/kkkls/EVSSM 创新点 提出了高效视觉状态空间模型…...

动态规划--斐波那契类型

目录 前言 1 第N个斐波那契数 2 爬楼梯 3 三步问题 4 使用最小花费爬楼梯 5 解码方法 总结 前言 本篇所讲的几个题目都是与斐波那契数的做法与思路类似的题目&#xff0c;所以直接放在一块解决了。 同时&#xff0c;由于第一次接触动态规划&#xff0c;我们也会讲解一…...

supervisord管理Gunicorn进程,使用Nginx作为反向代理运行flask web项目

1. 安装 Gunicorn 在项目虚拟环境中安装 Gunicorn&#xff1a;2. 基本用法 配置文件 创建一个 Gunicorn 配置文件&#xff08;如 gunicorn_config.py&#xff09;&#xff0c;方便管理复杂配置。 示例 gunicorn_config.py&#xff1a; bind "0.0.0.0:8000" #…...

《Python实战进阶》No16: Plotly 交互式图表制作指南

No16: Plotly 交互式图表制作指南 Plotly是一款用来做数据分析和可视化的在线平台&#xff0c;功能真的是非常强大&#xff0c;它主要有以下特点&#xff1a; 图形多样化&#xff1a;在线绘制多种图形&#xff0c;比如柱状图、饼图、直方图、饼图、气泡图、桑基图、股票图、旭…...

代码随想录算法训练营第22天 | 组合总和 分割回文串

39. 组合总和 39. 组合总和 - 力扣&#xff08;LeetCode&#xff09; 题目链接/文章讲解&#xff1a;代码随想录 视频讲解&#xff1a;带你学透回溯算法-组合总和&#xff08;对应「leetcode」力扣题目&#xff1a;39.组合总和&#xff09;| 回溯法精讲&#xff01;_哔哩哔哩_…...

DeepSeek 医疗大模型微调实战讨论版(第一部分)

DeepSeek医疗大模型微调实战指南第一部分 DeepSeek 作为一款具有独特优势的大模型,在医疗领域展现出了巨大的应用潜力。它采用了先进的混合专家架构(MoE),能够根据输入数据的特性选择性激活部分专家,避免了不必要的计算,极大地提高了计算效率和模型精度 。这种架构使得 …...

Linux云计算SRE-第十七周

1. 做三个节点的redis集群。 1、编辑redis节点node0(10.0.0.100)、node1(10.0.0.110)、node2(10.0.0.120)的安装脚本 [rootnode0 ~]# vim install_redis.sh#!/bin/bash # 指定脚本解释器为bashREDIS_VERSIONredis-7.2.7 # 定义Redis的版本号PASSWORD123456 # 设置Redis的访问…...