羊、狼、农夫过河

一、题目描述

羊、狼、农夫都在岸边,当羊的数量小于狼的数量时,狼会攻击羊,农夫则会损失羊。农夫有一艘容量固定的船,能够承载固定数量的动物。

要求求出不损失羊情况下将全部羊和狼运到对岸需要的最小次数。

只计算农夫去对岸的次数,回程时农夫不会运送羊和狼。

备注:农夫在或农夫离开后羊的数量大于狼的数量时狼不会攻击羊。

二、输入输出描述

输入描述
  • 第一行:三个整数M,N,X, 分别代表羊的数量,狼的数量,小船的容量。
输出描述
  • 一个整数,表示安全运输所有羊和狼到对岸的最小次数;若无法完成则输出 0。

三、示例

|----|-----------------------------|
| 输入 | 5 3 3 |
| 输出 | 3 |
| 说明 | 第一次运2只狼 第二次运3只羊 第三次运2只羊和1只狼 |

|----|--------------------|
| 输入 | 5 4 1 |
| 输出 | 0 |
| 说明 | 如果找不到不损失羊的运送方案,输出0 |

四、解题思路

1. 核心思想

采用DFS 枚举 + 剪枝优化的策略,遍历所有合法的 "单次运送组合",通过递归探索完整的运送路径,记录所有能将羊狼全运完的次数,最终取最小值。核心是 "枚举所有可能,剪枝排除不安全 / 无意义的路径,保证只探索有效解"。

2. 问题本质分析
  • 表层问题:找到羊狼过河的最少运送次数;
  • 深层问题:
  1. 状态空间搜索:每个 "本岸羊狼数 + 对岸羊狼数" 是一个状态,需遍历从初始状态(本岸 m/n,对岸 0/0)到终止状态(本岸 0/0)的所有合法路径;
  2. 安全约束是核心:任意岸的羊 > 0 时必须羊 > 狼,否则路径无效;
  3. 剪枝的必要性:若不剪枝,会枚举大量不安全 / 无意义的组合(如空运、超载),导致递归效率极低;
  4. 最少次数的本质:DFS 会遍历所有合法路径,最终取次数最小值,等价于 "广度优先搜索(BFS)找最短路径",但 DFS 通过记录所有次数再取最小实现。
3. 核心逻辑
  • 状态枚举:双重循环枚举单次运送的羊、狼数量组合;
  • 安全校验:校验本岸、对岸运送后的状态是否安全;
  • 剪枝优化:提前排除空运、超载、不安全、后续无法挽救的组合;
  • 递归探索:合法组合则递归更新状态,继续探索下一次运送;
  • 结果收集:到达终止状态时记录次数,最终返回最小值。
4. 步骤拆解
  1. 初始状态初始化

    • 输入本岸羊数sheep、狼数wolf、船负载boat
    • 初始化结果集合ans,启动 DFS(初始对岸 0 羊 0 狼,次数 0)。
  2. DFS 终止条件判断

    • 若本岸羊狼都为 0:将当前次数加入ans,返回;
    • 若剩余羊狼总数≤船负载:将次数 + 1 加入ans,返回(一次运完)。
  3. 枚举单次运送组合

    • 外层循环:枚举船上羊数i(0 到min(boat, sheep));
    • 内层循环:枚举船上狼数j(0 到min(boat, wolf))。
  4. 剪枝过滤无效组合

    • 排除空运(i+j=0)、超载(i+j>boat);
    • 排除本岸运送后不安全的组合(剩余羊 > 0 且羊≤狼);
    • 排除对岸运送后不安全的组合(新增羊 > 0 且羊≤狼);
    • 排除对岸无羊但狼≥船负载的组合(后续无法挽救)。
  5. 递归探索下一次运送

    • 更新本岸:羊数sheep-i,狼数wolf-j
    • 更新对岸:羊数oppo_sheep+i,狼数oppo_wolf+j
    • 次数 + 1,继续递归。
  6. 结果处理

    • 所有递归结束后,若ans非空,返回最小值(最少次数);
    • 若无合法解,返回 0。

五、代码实现

java 复制代码
import java.util.ArrayList;
import java.util.Collections;
import java.util.Scanner;

public class Main {
  public static void main(String[] args) {
    Scanner sc = new Scanner(System.in);

    int m = sc.nextInt();
    int n = sc.nextInt();
    int x = sc.nextInt();

    System.out.println(getResult(m, n, x));
  }

  /**
   * @param sheep 本岸羊数量
   * @param wolf 本岸狼数量
   * @param boat 船负载
   * @return 最少运送次数
   */
  public static int getResult(int sheep, int wolf, int boat) {
    ArrayList<Integer> ans = new ArrayList<>();
    dfs(sheep, wolf, boat, 0, 0, 0, ans);

    if (ans.size() > 0) {
      return Collections.min(ans);
    } else {
      return 0;
    }
  }

  public static void dfs(
      int sheep,
      int wolf,
      int boat,
      int oppo_sheep,
      int oppo_wolf,
      int count,
      ArrayList<Integer> ans) {
    if (sheep == 0 && wolf == 0) {
      ans.add(count);
      return;
    }

    if (sheep + wolf <= boat) {
      ans.add(count + 1);
      return;
    }

    // i 代表船上羊数量,最多Math.min(boat, sheep)
    for (int i = 0; i <= Math.min(boat, sheep); i++) {
      // j 代表船上狼数量,最多Math.min(boat, wolf)
      for (int j = 0; j <= Math.min(boat, wolf); j++) {
        // 空运
        if (i + j == 0) continue;
        // 超载
        if (i + j > boat) break;

        // 本岸羊 <= 本岸狼,说明狼运少了
        if (sheep - i <= wolf - j && sheep - i != 0) continue;

        // 对岸羊 <= 对岸狼,说明狼运多了
        if (oppo_sheep + i <= oppo_wolf + j && oppo_sheep + i != 0) break;

        // 对岸没羊,但是对岸狼已经超过船载量,即下次即使整船都运羊,也无法保证对岸羊 > 对岸狼
        if (oppo_sheep + i == 0 && oppo_wolf + j >= boat) break;

        dfs(sheep - i, wolf - j, boat, oppo_sheep + i, oppo_wolf + j, count + 1, ans);
      }
    }
  }
}
相关推荐
java资料站2 小时前
SpringAI+DeepSeek大模型应用开发实战
java
不会写代码0002 小时前
Flutter 框架跨平台鸿蒙开发 - 待办事项优先级排序开发教程
flutter·华为·harmonyos
Elastic 中国社区官方博客2 小时前
使用瑞士风格哈希表实现更快的 ES|QL 统计
大数据·数据结构·sql·elasticsearch·搜索引擎·全文检索·散列表
进击的CJR2 小时前
redis cluster 部署
java·redis·mybatis
重生之后端学习2 小时前
19. 删除链表的倒数第 N 个结点
java·数据结构·算法·leetcode·职场和发展
qq_12498707532 小时前
基于小程序中医食谱推荐系统的设计(源码+论文+部署+安装)
java·spring boot·后端·微信小程序·小程序·毕业设计·计算机毕业设计
小白阿龙2 小时前
鸿蒙+Flutter 跨平台开发——防止预测的真随机密码生成器设计
flutter·华为·harmonyos·鸿蒙
Coder_Boy_2 小时前
基于SpringAI的在线考试系统-阅卷评分模块时序图
java·人工智能·spring boot
aini_lovee2 小时前
严格耦合波(RCWA)方法计算麦克斯韦方程数值解的MATLAB实现
数据结构·算法·matlab