端点石子移动问题 | 豆包MarsCode AI刷题

68 阅读6分钟

端点石子移动问题 | 豆包MarsCode AI刷题

问题描述

小S正在玩一个关于石子的游戏,游戏中给定了一些石子,它们位于一维数轴上的不同位置,位置用数组 stones 表示。如果某个石子处于最小或最大的一个位置,我们称其为端点石子。

在每个回合,小S可以将一颗端点石子移动到一个未占用的位置,使其不再是端点石子。值得注意的是,如果石子的位置是连续的,则游戏结束,因为没有可以进行的移动操作。

你需要帮助小S找到可以移动的最小次数。


测试样例

样例1:

输入:stones = [7, 4, 9] 输出:1

样例2:

输入:stones = [6, 5, 4, 3, 10] 输出:2

样例3:

输入:stones = [1, 2, 3, 4, 5] 输出:0

题目解析

题目解析
  1. 端点石子 在游戏中,位于数轴上最小或最大位置的石子称为端点石子,例如 [7, 4, 9] 中的 49 是端点石子。
  2. 游戏规则 每回合只能将端点石子移动到一个未被占用的位置,直到所有石子的位置是连续的。
  3. 目标 找到将石子移动至连续位置所需的最小移动次数

思路分析

核心思路:滑动窗口法

  1. 石子连续的判断 如果石子已经是连续的(即 stones[n-1] - stones[0] + 1 == n),则无需移动,返回 0

  2. 最小移动次数计算(滑动窗口)

    • 对排序后的石子位置,维护一个滑动窗口 [i, j],其中窗口内所有石子连续。
    • 在窗口内计算已经就位的石子数 rightPlace = j - i
    • 缺失的石子数即为当前需要的移动次数:n - rightPlace
    • 特殊情况:如果窗口内石子数达到 n - 1 且区间正好只差一个空位(即 stones[j-1] - stones[i] + 1 == n - 1),最小移动次数为 2

算法步骤
  1. 数组排序

    • 确保石子按照数轴顺序排列。
  2. 滑动窗口

    • 从左到右遍历每个石子的起始位置 i,保持窗口内的最大范围 stones[j] - stones[i] + 1 <= n
    • 对每个窗口计算 rightPlace,更新最小移动次数。
  3. 返回结果

    • 返回计算出的最小移动次数。

Java 实现代码
 import java.util.*;
 ​
 public class Main {
     public static int solution(int[] stones) {
         if (stones.length == 1) {
             return 0; // 单个石子,已经连续
         }
         Arrays.sort(stones); // 排序石子位置
         int n = stones.length;
 ​
         // 滑动窗口计算最小移动次数
         int minCount = (int) 1e9;
         int j = 0; // 窗口右边界
         for (int i = 0; i < n; i++) {
             // 扩展窗口,直到窗口内石子数大于 n
             while (j<n && stones[j]-stones[i]+1<=n) {
                 j++;
             }
             int rightPlace = j - i; // 窗口内已经就位的石子数
             
             // 检查特殊情况
             if (rightPlace==n-1 && stones[j-1]-stones[i]+1 == n-1) {
                 minCount = Math.min(minCount, 2); // 特殊情况,最少需要两步
             } else {
                 minCount = Math.min(minCount, n - rightPlace); // 一般情况
             }
         }
         return minCount; // 返回最小移动次数
     }
 ​
     public static void main(String[] args) {
         System.out.println(solution(new int[]{7, 4, 9})); // 输出: 1
         System.out.println(solution(new int[]{6, 5, 4, 3, 10})); // 输出: 2
         System.out.println(solution(new int[]{1, 2, 3, 4, 5})); // 输出: 0
     }
 }

代码解析
  1. 输入与初始化

    • stones.length == 1 时,直接返回 0
    • 使用 Arrays.sort() 对石子位置排序。
  2. 滑动窗口

    • 遍历起始位置 i,扩展窗口右边界 j,确保窗口内石子数满足连续条件 stones[j] - stones[i] + 1 <= n
    • 计算当前窗口内的就位石子数 rightPlace,并更新最小移动次数。
  3. 特殊情况

    • 当窗口覆盖 n-1 个石子,且间隔仅差 1,则需要两步解决。

复杂度分析
  1. 时间复杂度

    • 排序:O(nlog⁡n)
    • 滑动窗口:O(n)
    • 总复杂度:O(nlog⁡n)
  2. 空间复杂度

    • 常量空间:O(1)
总结
  • 核心思想:通过滑动窗口的方式,计算当前窗口内石子数量和缺失石子数量,更新最小移动次数。
  • 特殊情况处理:对于窗口覆盖 n-1 且仅差一位的情况,直接返回 2
  • 时间复杂度:排序和遍历的结合,效率较高,适用于中等规模数据。

功能亮点剖析:AI 刷题实践的独特价值

1. 精选真题

精选真题为刷题过程提供了高质量、典型化的题目,通过对重点题型的练习,能帮助学习者快速掌握解题思路和常用算法。例如,针对“石子游戏”这类动态规划和滑动窗口结合的经典问题,AI 会根据题目分类和难度层级提供合理的练习路径,避免无效练习。

2. 云端编辑器

云端编辑器直接在网页或软件内运行代码并验证结果,不需要繁琐的开发环境配置。对于 Java 的解决方案,直接输入代码并点击运行,立刻获得反馈。云端编译的特点包括:

  • 随时测试:快速调试代码,验证每一部分逻辑。
  • 多语言支持:学习者可以切换多种语言,比较不同语言的实现思路。
  • 自动错误检测:对于常见错误(如数组越界、空指针),即时提示并帮助纠正。
3. 个性化题目推荐

AI 会根据学习者的练习记录,推荐适合其水平的题目。比如,如果用户熟悉排序问题和滑动窗口,但对动态规划较为陌生,系统会优先推荐难度较低、涉及动态规划思想的题目,逐步提升。


刷题实践:总结与案例分析

优势
  1. 高效学习:针对每道题目,AI 可以给出详细的解题思路、优化方案和复杂度分析。
  2. 逐步进阶:难度由浅入深,避免初学者因为难度跳跃性过大而放弃。
  3. 实时反馈:在代码运行失败时,AI 可以提供提示,例如指出数组未排序可能引起的问题。
  4. 多角度解答:对于同一问题,AI 会提供多种解决方案,如暴力法、滑动窗口优化、动态规划等,帮助学习者开阔思路。

实践案例

以下是一个使用 AI 刷题过程的分析:

题目:石子游戏最小移动次数

1. 初步解答

  • 用户写出了一份初步代码,但对滑动窗口的结束条件处理不清晰,结果出错。AI 提供了即时提示,并指出“j 超出数组范围”是导致错误的关键。

2. 提供优化思路

  • AI 建议改用“stones[j - 1] - stones[i] + 1”来检测窗口范围是否满足条件,并减少了不必要的遍历操作,优化时间复杂度。

3. 比较学习

  • 除了滑动窗口法,AI 提供了基于双指针的另一种解法,使学习者能够对比不同算法的实现差异。

总结

AI 刷题实践通过精选真题、智能反馈和多种解法的对比学习,帮助用户快速掌握算法精髓并提升编程能力。以“石子游戏”为例,通过云端测试和 AI 的解题引导,用户可以从零到熟练地掌握滑动窗口的应用,从而为其他类似问题的解答打下基础。