华为OD机试真题---狼羊过河

关于华为OD机试真题中的“狼羊过河”问题,实际上更准确地描述应该是“羊、狼、农夫过河”问题。这个问题是一个经典的逻辑问题,要求在满足特定条件下,利用一艘有限容量的船将羊和狼全部安全地运送到对岸。以下是对该问题的详细解析:

一、题目描述

农夫带着m只羊和n只狼过河,农夫有一条可载x只羊或狼的船。农夫在时或者羊的数量大于狼的数量时,狼不会攻击羊。农夫需要在不损失羊的情况下,计算出完成运输所需的最小次数(只计算农夫去对岸的次数,回程不计入次数)。

二、输入描述

输入参数为m,n,x;其中m为羊的数量,n为狼的数量,x为船可载羊和狼的数量(农夫不占用船的容量)。

三、输出描述

返回在不损失羊的情况下,将全部羊和狼运到对岸需要的最小次数。如果无法满足条件,则返回0。

四、解题思路

  1. 理解条件:首先,需要明确狼吃羊的条件是羊的数量小于狼的数量,且农夫不在场。因此,需要保证在运输过程中,任何时候留在本岸的羊的数量都不小于狼的数量,或者农夫在场。
  2. 分析船的容量:船的容量x是一个关键因素,它决定了每次可以运输的动物数量。需要根据x的奇偶性来制定不同的运输策略。
  3. 制定策略
    • 当船的容量x为偶数时,可以尽量保证每次运输时,对岸和本岸的羊的数量都大于狼的数量。例如,可以先运输一部分羊到对岸,再运输一部分狼到对岸,但保证每次运输后,本岸的羊的数量仍然大于狼的数量。
    • 当船的容量x为奇数时,策略可能需要更加复杂,因为需要更加精细地控制每次运输的动物数量和种类,以确保在任何时候都不会发生狼吃羊的情况。
  4. 计算最小次数:在制定了运输策略后,需要计算出完成运输所需的最小次数。这通常需要通过遍历所有可能的运输方案,找到满足条件且次数最少的方案。

五、代码实现

递归方法:

import java.util.HashMap;
import java.util.Map;public class WolfSheepFarmer {// 定义状态:0表示本岸,1表示对岸private static final int THIS_SHORE = 0;private static final int OTHER_SHORE = 1;private static int totalSheep;private static int totalWolf;// 使用Map来存储已经计算过的状态private static Map<String, Boolean> memo = new HashMap<>();// 最大递归深度private static final int MAX_RECURSION_DEPTH = 1000;// 检查当前状态是否安全private static boolean isSafe(int sheepThisShore, int wolfThisShore, int farmer) {if (farmer == THIS_SHORE) {// 农夫在本岸,检查狼的数量是否不大于羊的数量return sheepThisShore >= wolfThisShore || sheepThisShore == 0;} else {// 农夫在对岸,检查对岸的狼的数量是否不大于羊的数量int sheepOtherShore = totalSheep - sheepThisShore;int wolfOtherShore = totalWolf - wolfThisShore;return sheepOtherShore >= wolfOtherShore || sheepOtherShore == 0;}}// 尝试所有可能的过河方式private static boolean canCross(int sheepThisShore, int wolfThisShore, int farmer, int boatCapacity, int[] moves, int depth) {// 如果所有羊和狼都已经过河,返回trueif (sheepThisShore == 0 && wolfThisShore == 0) {return true;}// 超过最大递归深度,返回falseif (depth > MAX_RECURSION_DEPTH) {return false;}// 生成当前状态的唯一标识String stateKey = sheepThisShore + "," + wolfThisShore + "," + farmer;if (memo.containsKey(stateKey)) {return memo.get(stateKey);}// 尝试所有可能的过河组合for (int i = 0; i <= boatCapacity; i++) {for (int j = 0; j <= boatCapacity - i; j++) {// 农夫必须过河if (i + j == 0) continue;// 计算过河后的状态int newSheepThisShore = sheepThisShore - i;int newWolfThisShore = wolfThisShore - j;int newFarmer = (farmer == THIS_SHORE) ? OTHER_SHORE : THIS_SHORE;// 检查过河后的状态是否安全if (isSafe(newSheepThisShore, newWolfThisShore, newFarmer)) {// 记录这次过河动作moves[0]++; // 记录总动作次数(包括回程)if (newFarmer == OTHER_SHORE) {// 如果农夫到了对岸,则记录有效过河次数moves[1]++;}// 递归尝试下一步if (canCross(newSheepThisShore, newWolfThisShore, newFarmer, boatCapacity, moves, depth + 1)) {memo.put(stateKey, true);return true;}// 回溯:撤销过河动作moves[0]--;if (newFarmer == OTHER_SHORE) {moves[1]--;}}}}// 没有找到可行的过河方案memo.put(stateKey, false);return false;}public static int minCrosses(int sheep, int wolf, int boatCapacity) {totalSheep = sheep;totalWolf = wolf;int[] moves = {0, 0}; // 第一个元素记录总动作次数(包括回程),第二个元素记录有效过河次数// 初始状态:所有羊和狼都在本岸,农夫也在本岸if (canCross(sheep, wolf, THIS_SHORE, boatCapacity, moves, 0)) {// 返回有效过河次数(只计算农夫去对岸的次数)return moves[1];} else {// 无法完成过河return 0;}}public static void main(String[] args) {int sheep = 5;int wolf = 3;int boatCapacity = 3;int result = minCrosses(sheep, wolf, boatCapacity);System.out.println("最小过河次数(农夫去对岸的次数):" + result);}
}

广度优先搜索(BF)

import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;
import java.util.Set;public class WolfSheepFarmer {// 定义状态:0表示本岸,1表示对岸private static final int THIS_SHORE = 0;private static final int OTHER_SHORE = 1;// 使用Map来存储已经计算过的状态private Map<String, Boolean> memo;// 总羊和狼的数量private int totalSheep;private int totalWolf;// 最大递归深度private static final int MAX_RECURSION_DEPTH = 1000;public WolfSheepFarmer() {memo = new HashMap<>();}// 检查当前状态是否安全private boolean isSafe(int sheepThisShore, int wolfThisShore, int farmer) {if (farmer == THIS_SHORE) {// 农夫在本岸,检查狼的数量是否不大于羊的数量return sheepThisShore >= wolfThisShore || sheepThisShore == 0;} else {// 农夫在对岸,检查对岸的狼的数量是否不大于羊的数量int sheepOtherShore = totalSheep - sheepThisShore;int wolfOtherShore = totalWolf - wolfThisShore;return sheepOtherShore >= wolfOtherShore || sheepOtherShore == 0;}}// 尝试所有可能的过河方式private boolean canCross(int sheepThisShore, int wolfThisShore, int farmer, int boatCapacity, int[] moves, int depth) {// 如果所有羊和狼都已经过河,返回trueif (sheepThisShore == 0 && wolfThisShore == 0) {return true;}// 超过最大递归深度,返回falseif (depth > MAX_RECURSION_DEPTH) {return false;}// 生成当前状态的唯一标识String stateKey = sheepThisShore + "," + wolfThisShore + "," + farmer;if (memo.containsKey(stateKey)) {return memo.get(stateKey);}// 尝试所有可能的过河组合for (int i = 0; i <= boatCapacity; i++) {for (int j = 0; j <= boatCapacity - i; j++) {// 农夫必须过河if (i + j == 0) continue;// 计算过河后的状态int newSheepThisShore = sheepThisShore - i;int newWolfThisShore = wolfThisShore - j;int newFarmer = (farmer == THIS_SHORE) ? OTHER_SHORE : THIS_SHORE;// 检查过河后的状态是否安全if (isSafe(newSheepThisShore, newWolfThisShore, newFarmer)) {// 记录这次过河动作moves[0]++; // 记录总动作次数(包括回程)if (newFarmer == OTHER_SHORE) {// 如果农夫到了对岸,则记录有效过河次数moves[1]++;}// 递归尝试下一步if (canCross(newSheepThisShore, newWolfThisShore, newFarmer, boatCapacity, moves, depth + 1)) {memo.put(stateKey, true);return true;}// 回溯:撤销过河动作moves[0]--;if (newFarmer == OTHER_SHORE) {moves[1]--;}}}}// 没有找到可行的过河方案memo.put(stateKey, false);return false;}// 使用递归方法求解最小过河次数public int minCrossesRecursive(int sheep, int wolf, int boatCapacity) {if (sheep < 0 || wolf < 0 || boatCapacity <= 0) {throw new IllegalArgumentException("Invalid input parameters");}totalSheep = sheep;totalWolf = wolf;int[] moves = {0, 0}; // 第一个元素记录总动作次数(包括回程),第二个元素记录有效过河次数// 初始状态:所有羊和狼都在本岸,农夫也在本岸if (canCross(sheep, wolf, THIS_SHORE, boatCapacity, moves, 0)) {// 返回有效过河次数(只计算农夫去对岸的次数)return moves[1];} else {// 无法完成过河return 0;}}// 使用广度优先搜索(BFS)求解最小过河次数public int minCrossesBFS(int sheep, int wolf, int boatCapacity) {if (sheep < 0 || wolf < 0 || boatCapacity <= 0) {throw new IllegalArgumentException("Invalid input parameters");}totalSheep = sheep;totalWolf = wolf;// 使用队列进行广度优先搜索Queue<int[]> queue = new LinkedList<>();Set<String> visited = new HashSet<>();// 初始状态:所有羊和狼都在本岸,农夫也在本岸int[] initialState = {sheep, wolf, THIS_SHORE, 0, 0};String initialStateKey = getStateKey(initialState);queue.offer(initialState);visited.add(initialStateKey);while (!queue.isEmpty()) {int[] currentState = queue.poll();int sheepThisShore = currentState[0];int wolfThisShore = currentState[1];int farmer = currentState[2];int totalMoves = currentState[3];int validMoves = currentState[4];// 如果所有羊和狼都已经过河,返回有效过河次数if (sheepThisShore == 0 && wolfThisShore == 0) {return validMoves;}// 尝试所有可能的过河组合for (int i = 0; i <= boatCapacity; i++) {for (int j = 0; j <= boatCapacity - i; j++) {// 农夫必须过河if (i + j == 0) continue;// 计算过河后的状态int newSheepThisShore = sheepThisShore - i;int newWolfThisShore = wolfThisShore - j;int newFarmer = (farmer == THIS_SHORE) ? OTHER_SHORE : THIS_SHORE;// 检查过河后的状态是否安全if (isSafe(newSheepThisShore, newWolfThisShore, newFarmer)) {int[] newState = {newSheepThisShore, newWolfThisShore, newFarmer, totalMoves + 1, validMoves + (newFarmer == OTHER_SHORE ? 1 : 0)};String newStateKey = getStateKey(newState);// 如果新状态未被访问过,加入队列if (!visited.contains(newStateKey)) {queue.offer(newState);visited.add(newStateKey);}}}}}// 无法完成过河return 0;}// 生成状态的唯一标识private String getStateKey(int[] state) {return state[0] + "," + state[1] + "," + state[2];}public static void main(String[] args) {int sheep = 5;int wolf = 3;int boatCapacity = 3;WolfSheepFarmer solver = new WolfSheepFarmer();// 使用递归方法求解int resultRecursive = solver.minCrossesRecursive(sheep, wolf, boatCapacity);System.out.println("最小过河次数(农夫去对岸的次数,递归方法):" + resultRecursive);// 使用BFS方法求解int resultBFS = solver.minCrossesBFS(sheep, wolf, boatCapacity);System.out.println("最小过河次数(农夫去对岸的次数,BFS方法):" + resultBFS);}
}

注意

递归方法和BFS方法在某些情况下可能会产生不同的结果,原因在于它们的搜索策略不同。递归方法是一种深度优先搜索(DFS),而BFS方法则是广度优先搜索。这两种方法在搜索过程中可能会找到不同的最优路径。

问题分析

递归方法:

  • 递归方法使用深度优先搜索(DFS),可能会找到一个解,但不一定是最优解。
  • 递归方法依赖于记忆化来避免重复计算,但如果路径选择不当,可能会陷入较深的递归层级,从而错过最优解。
    BFS方法:
  • BFS方法使用广度优先搜索,确保找到最短路径(即最少的过河次数)。
  • BFS方法通常能找到最优解,因为它逐层扩展节点,确保最先到达目标状态的是最短路径。

六、运行示例解析

示例输入
  • 羊的数量:5
  • 狼的数量:3
  • 船的容量:3
运行过程解析
  1. 初始化

    • totalSheep 被设置为 5,totalWolf 被设置为 3。
    • memo Map 被初始化为空,用于存储已经计算过的状态。
    • moves 数组被初始化为 {0, 0},分别记录总动作次数(包括回程)和有效过河次数(农夫到对岸的次数)。
  2. 开始递归搜索

    • 从初始状态开始,即所有羊和狼都在本岸(sheepThisShore = 5, wolfThisShore = 3),农夫也在本岸(farmer = THIS_SHORE)。
    • 调用 canCross 方法,传入当前状态、船的容量、moves 数组和递归深度(初始为 0)。
  3. 递归搜索过程

    • canCross 方法中,首先检查是否所有羊和狼都已经过河(即 sheepThisShore == 0 && wolfThisShore == 0)。如果不是,则继续搜索。
    • 检查当前递归深度是否超过最大深度限制(MAX_RECURSION_DEPTH = 1000)。如果没有超过,则继续。
    • 使用当前状态(羊、狼、农夫的位置)生成一个唯一的状态键(stateKey),并检查该状态是否已经在 memo 中被计算过。如果是,则直接返回结果。
    • 如果没有计算过,则尝试所有可能的过河组合(羊的数量 i 和狼的数量 j,其中 i + j 必须小于等于船的容量,且 i + j 不能为 0,因为农夫必须过河)。
    • 对于每种过河组合,计算过河后的新状态,并检查新状态是否安全(即农夫在对岸时,对岸的羊数量不少于狼数量;农夫在本岸时,本岸的羊数量不少于狼数量)。
    • 如果新状态安全,则记录过河动作(增加 moves[0]),如果农夫到了对岸,则增加有效过河次数(moves[1])。
    • 递归调用 canCross 方法,传入新状态和增加的递归深度。
    • 如果递归调用返回 true,则表示找到了一种可行的过河方案,将当前状态标记为已计算(memo.put(stateKey, true)),并返回 true
    • 如果递归调用返回 false,则回溯(撤销过河动作),并继续尝试其他过河组合。
  4. 找到解决方案

    • 经过多次递归调用和回溯,最终会找到一种(或多种,但只关心找到的第一种)可行的过河方案。
    • 当找到可行的过河方案时,moves[1] 将包含农夫到对岸的最小次数。
  5. 输出结果

    • main 方法中,调用 minCrosses 方法,并打印结果。
    • 输出结果为:“最小过河次数(农夫去对岸的次数):4”,其中 4 是找到的最小次数。
示例输出

由于这个问题是一个复杂的搜索问题,并且涉及大量的状态空间和可能的过河组合,因此无法手动计算具体的输出。但是,当您运行这段代码时,它将输出一个整数,表示农夫到对岸的最小次数,以便将所有羊和狼安全地运送到对岸。

请注意,由于状态空间可能非常大,这个问题可能需要一些时间来解决,特别是当羊和狼的数量以及船的容量增加时。此外,MAX_RECURSION_DEPTH 的设置可能会影响搜索的效率和是否能够找到解决方案。如果设置得太低,可能会错过解决方案;如果设置得太高,可能会导致性能问题。

七、总结

“羊、狼、农夫过河”问题是一个经典的逻辑问题,需要仔细分析题目要求、理解条件、制定策略并计算出最小次数。在华为OD机试中,这类问题通常用于考察应聘者的逻辑思维能力和问题解决能力。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Linux系统编程】第三十九弹---探索信号处理的奥秘:阻塞信号与sigset_t的深入剖析及实战

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】【C详解】【Linux系统编程】 目录 1、信号处理 2、阻塞信号 2.1、信号其他相关常见概念 2.2、在内核中的表示 2.3、sigset_t 2.4、信号集操作函数 3、完整…

QNAP威联通NAS设置了仅允许列表内的计算机连接,但是忘记有效IP地址怎么访问

创作立场&#xff1a;原创不易&#xff0c;拒绝搬运~ hello 大家好&#xff0c;我是你们的老伙伴&#xff0c;稳重的大王~ 本期教程为大家介绍一个使用场景的故障解除办法&#xff0c;如果遇到类似问题&#xff0c;可以参考一下解题思路~ 如上图所示位置&#xff0c;不小心设…

LabVIEW非接触式模态参数识别系统开发

基于LabVIEW的模态参数识别系统采用非接触式声学方法&#xff0c;结合LabVIEW软件和高精度硬件&#xff0c;实现机械结构模态参数的快速准确识别。降低了模态分析技术门槛&#xff0c;提高测试效率和准确性。 项目背景与意义: 传统的模态分析方法&#xff0c;如锤击法&#x…

ssm011线上旅行信息管理系统(论文+源码)_kaic

毕业论文&#xff08;设计&#xff09; 题 目&#xff1a;线上旅行信息管理系统设计与实现 姓  名&#xff1a; 专  业&#xff1a; 班  级&#xff1a; …

【RAG实战】优化索引的四种高级方法,快点收藏起来!!

前言 Indexing&#xff08;索引&#xff09;是搭建任何RAG系统的第一步&#xff0c;也是至关重要的一步&#xff0c;良好的索引意味着合理的知识或信息分类&#xff0c;召回环节就会更加精准。在这篇文章中&#xff0c;围绕Indexing&#xff08;索引&#xff09;环节&#xff…

【火山引擎】文生图实践 | PYTHON

目录 1 准备工作 2 实践 1 准备工作 ① 服务开通 确保已开通需要访问的服务。可前往火山引擎控制台并开通相应服务。 ② 获取安全凭证 Access Key (访问密钥)

北京迅为iTOP-LS2K0500开发板快速使用编译环境虚拟机Ubuntu基础操作及设置

迅为iTOP-LS2K0500开发板 迅为iTOP-LS2K0500开发板采用龙芯LS2K0500处理器&#xff0c;基于龙芯自主指令系统&#xff08;LoongArch&#xff09;架构&#xff0c;片内集成64位LA264处理器核、32位DDR3控制器、2D GPU、DVO显示接口、两路PClE2.0、两路SATA2.0、四路USB2.0、一路…

浏览器HTTP缓存解读(HTTP Status:200 304)

为什么要有浏览器缓存&#xff1f; 浏览器缓存(Brower Caching)是浏览器对之前请求过的文件进行缓存&#xff0c;以便下一次访问时重复使用&#xff0c;节省带宽&#xff0c;提高访问速度&#xff0c;降低服务器压力 http缓存机制主要在http响应头中设定&#xff0c;响应头中…

(蓝桥杯C/C++)——常用库函数

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 一、 二分查找 1.二分查找的前提 2.binary_ search函数 3.lower_bound和upper_bound 二、排序 1.sort概念 2.sort的用法 3.自定义比较函数 三、全排列 1.next p…

Spring Boot⾃动配置

一、Spring Boot的自动配置原理 Spring Boot使用一种称为“约定优于配置”的方法&#xff0c;这意味着如果你按照预定的方式来安排你的代码和依赖项&#xff0c;Spring Boot可以自动配置你的应用程序。主要特点包括&#xff1a; 自动检测&#xff1a;Spring Boot在应用启动时…

C#实现word和pdf格式互转

1、word转pdf 使用nuget&#xff1a; Microsoft.Office.Interop.Word winform页面&#xff1a; 后端代码&#xff1a; //using Spire.Doc; //using Spire.Pdf; using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using Sy…

iOS Swift5算法恢复——HMAC

demangle的时候看到了CryptoSwift&#xff0c;HMAC&#xff0c;于是写一个helloworld&#xff0c;用于对照。 sudo gem install cocoapods pod init pods文件&#xff0c;注意要标注静态链接&#xff1a; # Uncomment the next line to define a global platform for your p…

Diving into the HAL-----HAL_GPIO

1、怎么看待外设&#xff1a; 从总线连接的角度看&#xff0c;外设和Core、DMA通过总线交换数据&#xff0c;正所谓要想富先修路。要注意&#xff0c;这些总线中的每一个都连接到不同的时钟源&#xff0c;这些时钟源决定了连接到该总线的外设操作的最大速度。 从内存分配的角度…

C#与C++交互开发系列(十六):使用多线程

前言 在开发需要高性能的应用程序时&#xff0c;多线程是提升处理效率和响应速度的关键技术。C 和 C# 各自拥有不同的线程模型和并发工具。在跨语言开发中&#xff0c;如何有效地利用两者的并发特性&#xff0c;同时确保线程安全和数据一致性&#xff0c;是一个值得探讨的问题…

libavdevice.so.58: cannot open shared object file: No such file ordirectory踩坑

博主是将大图切分成小图时遇到 问题一、linux编译后&#xff0c;找不到ffmpeg中的一个文件 产生原因&#xff0c;各种包集成&#xff0c;然后安装以后乱七八糟&#xff0c;甚至官方的教程也不规范导致没有添加路径到系统文件导致系统执行的时候找不到 1.下载 博主进行的离线…

查询使用方法:

模糊查询&#xff1a; 查询某一列中内容为空的记录。 -- 模糊查询 (项目中尽量不要使用) -- 张x SELECT * FROM student WHERE student_name LIKE 张_; -- % 不限长度的字符 -- 手机号中有 23 0或多个 SELECT * FROM student WHERE phone LIKE %23% -- 名字包含铮的学生 SELEC…

数据结构与算法分析——你真的理解查找算法吗——基于散列的查找(代码详解+万字长文)

一、算法描述 前面讨论的查找算法在处理小数据量(顺序查找)或者有序的数据集合(二分查找)时才使用。我们需要更加强大的算法能够查找较大的集合,而且并不需要有序。最常使用的一个方法是使用散列函数来将目标元素的一个或者多个特征转换成一个值,这个值用来索引一个已经索引的…

密码管理工具实现

该文档详细描述了实现一个简单的密码管理工具的过程&#xff0c;工具基于PHP和MySQL构建&#xff0c;支持用户注册、密码存储、管理以及角色权限控制等核心功能。 系统架构设计 技术栈&#xff1a;PHP&#xff08;后端逻辑&#xff09;、MySQL&#xff08;数据存储&#xff09…

深度学习(七)深度强化学习:融合创新的智能之路(7/10)

一、深度强化学习的崛起 深度强化学习在人工智能领域的重要地位 深度强化学习作为一种融合了深度学习和强化学习的新技术&#xff0c;在人工智能领域占据着至关重要的地位。它结合了深度学习强大的感知能力和强化学习优秀的决策能力&#xff0c;能够处理复杂的任务和环境。例如…

基于 Java 的 Spring Boot 和 Vue 的宠物领养系统设计与实现

需要代码 vx&#xff1a;Java980320 不收取任何费用 在这个宠物领养系统中&#xff0c;我们可以设定两个角色&#xff1a;管理员和普通用户。每个角色的功能和目标略有不同&#xff0c;以下分别介绍&#xff1a; 管理员 管理员的主要职责是确保平台的高效运行&#xff0c…