2024字节青训营笔记(七)石子移动问题、端点石子移动问题 | 豆包MarsCode AI刷题

263 阅读8分钟

石子移动问题

问题描述

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

在每个回合,小S可以将一颗端点石子移动到一个未占用的位置,使其不再是端点石子。游戏继续,直到石子的位置变得连续,无法再进行任何移动操作。

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


端点石子移动问题

问题描述

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

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

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


解题思路

这两道题目都涉及到石子在一维数轴上的移动问题,通过移动最终让所有石子连续,求解最大操作数最小操作数

我们先考虑最大操作次数,比较容易想到。

最大移动次数 (贪心)

最大移动次数问题

  • 目标:找到可以移动的最大次数。
  • 关键点:每次移动端点石子到一个未占用的位置,使其不再是端点石子。游戏继续,直到石子的位置变得连续,无法再进行任何移动操作。
  • 思路
    • 第一次我们只能选择 stones[0]stones[0] 或者 stones[n1]stones[n−1] 进行移动。因为移动后不能继续成为端点石子,所以若移动 stones[0]stones[0] ,则 stones[1]stones[1]stones[0]stones[0] 之间的空位将会被丢弃,若移动 stones[n1]stones[n−1] ,则 stones[n1]stones[n−1]stones[n2]stones[n−2] 之间的空位将会被丢弃。

    • 如果我们每次移动都将端点石子移到其最近的空位,则第一次移动后,若移动 stones[0]stones[0] ,则此时最左端的两个石子一定保证相邻,此时我们在之后的操作中都将最左端的石子移动到与之最近的空位中,直至不能进行操作,这样剩下的空位都不会被丢弃。

代码如下:

int solution(vector<int> &stones) {
    int n = stones.size(); // 获取石子的数量
    if (n == 1) return 0; // 如果只有一个石子,不需要移动,直接返回0

    sort(stones.begin(), stones.end()); // 对石子位置进行排序

    //计算如果移动最左边的端点石子,和移动最右边的端点石子,取两者中的最大值作为结果
    int ans=max(stones[n-2]-stones[0]+1-(n-1),stones[n-1]-stones[1]+1-(n-1));

    return ans; // 返回最大移动次数
}
  • 时间复杂度:O(nlogn)O(nlogn),主要为排序的时间复杂度

最小移动次数 (贪心、双指针)

最小移动次数问题

  • 先求出以每个点结尾时,长度为 nn 的区间能覆盖到当前最多的石子的数量,记为 mm
  • 可以通过双指针来求得。
  • 如果 mm 不等于 n1n - 1 ,则我们可以通过 nmn - m 次移动得到答案,这已经达到了答案的下限。
    1. m==nm == n ,答案为 00
    2. m<n1m < n - 1,有可能是两个端点不在这个区间内,也有可能是一端至少两个点不在区间内。这两种情况都可以通过贪心的移动构造,组成最后连续的区间。
      • 一端至少两个点不在区间内:先将1移动到3的位置上,再将2移动到4的位置上image.png
      • 两个端点不在这个区间内:先将8移动到2的位置上,再将1移动到3的位置上 image.png
  • 如果 mm 等于 n1n - 1
    • n1n-1 个石子中间有空隙:stones[0]+n1==stones[n2]stones[0] + n - 1 == stones[n - 2] 或者 stones[1]+n1==stones[n1]stones[1] + n - 1 == stones[n - 1]。这两种情况可以通过 1步 完成。
    • 其他情况,都需要 2步 完成。
      • n1n-1 个石子连续不间断,需要两步完成:image.png

代码如下:

int solution(vector<int>& stones) {
    // 对石子位置进行排序
    sort(stones.begin(), stones.end());
    int n = stones.size();

    int ans = 1e9; // 初始化最小移动次数为一个很大的数
    for (int i = 0, j = 0; i < n; i++) {
        // 移动左指针 j,使得区间 [j, i] 内的石子数量不超过 n
        while (stones[i] - stones[j] + 1 > n) j++;
        int m = i - j + 1; // 计算当前区间 [j, i] 内的石子数量

        // 如果当前区间内的石子数量为 n-1,并且区间内的石子位置是连续的
        if (m == n - 1 && stones[i] - stones[j] == i - j) {
            ans = min(ans, 2); // 只需要移动两次
        } else {
            ans = min(ans, n - m); // 否则,计算需要移动的最小次数
        }
    }

    return ans; // 返回最小移动次数
}
  • 时间复杂度:O(nlogn)O(nlogn),主要为排序的时间复杂度

总结

这两道题目都通过贪心算法来优化移动次数。最大移动次数问题通过每次移动端点石子到最近的空位,直到所有石子位置连续。最小移动次数问题通过双指针计算以每个点结尾时,长度为 n 的区间能覆盖到当前最多的石子的数量 m,并根据 m 的值来分类讨论,计算最小移动次数。

通过这两种方法,我们可以有效地解决石子移动问题,找到最大和最小移动次数。这两种方法的时间复杂度均为 O(nlogn),主要为排序的时间复杂度。通过合理的算法设计和实现,我们可以高效地解决这类问题。

知识总结与学习建议

新知识点梳理与分析

在使用豆包MarsCode AI刷题的过程中,我总结了以下几个新知识点:

  1. 贪心算法:

    • 理解:贪心算法是一种在每一步选择中都采取在当前状态下最好或最优的选择,从而希望导致结果是全局最好或最优的算法。
    • 应用:在石子移动问题中,贪心算法被用于优化移动次数,通过每次移动端点石子到最近的空位,直到所有石子位置连续。
  2. 双指针技巧:

    • 理解:双指针技巧是一种常用的算法技巧,通过两个指针在数组或链表中移动,来解决一些需要线性时间复杂度的问题。
    • 应用:在最小移动次数问题中,双指针技巧被用于计算以每个点结尾时,长度为 n 的区间能覆盖到当前最多的石子的数量 m
  3. 排序与优化:

    • 理解:排序是解决许多算法问题的第一步,通过排序可以简化后续的计算和优化过程。
    • 应用:在石子移动问题中,首先对石子位置进行排序,以便后续的贪心和双指针操作。

学习建议

对于其他入门同学,我有以下几点学习建议:

  1. 理解算法思想:

    • 在刷题过程中,不要急于求解,先理解题目的核心思想和算法思路。可以通过阅读题目描述、分析样例和参考解题思路来加深理解。
  2. 多练习:

    • 通过多次练习同一类型的题目,可以加深对算法和数据结构的理解。豆包MarsCode AI提供了丰富的题目库,可以根据自己的进度选择合适的题目进行练习。
  3. 总结与反思:

    • 每次刷题后,总结解题思路和遇到的问题,反思自己的解题过程。通过总结和反思,可以发现自己的不足,并在下次刷题时加以改进。

学习计划

高效学习方法

  1. 制定刷题计划:

    • 根据自己的时间和目标,制定一个合理的刷题计划。可以按照题目类型、难度等级或算法分类进行刷题,逐步提高自己的解题能力。
  2. 利用错题进行针对性学习:

    • 对于做错的题目,不要轻易放过。分析错误原因,找出解题思路中的漏洞,并进行针对性的学习和练习。豆包MarsCode AI提供了详细的解析和提示,可以帮助你更好地理解错题。
  3. 定期复习:

    • 定期复习之前做过的题目,巩固已有的知识。通过复习,可以加深对算法和数据结构的理解,并提高解题速度和准确率。

工具运用

结合其他学习资源

  1. AI刷题与教材结合:

    • 在刷题过程中,可以结合教材或在线课程,系统学习算法和数据结构的基础知识。豆包提供了丰富的题目和解析,可以帮助你更好地理解和应用所学知识。
  2. 社区交流:

    • 加入算法学习社区,与其他学习者交流经验和解题思路。通过社区交流,可以拓宽视野,发现新的解题方法和技巧。
  3. 实践项目:

    • 将所学的算法和数据结构应用到实际项目中,通过实践来巩固和提高自己的能力。豆包提供了丰富的题目和解析,可以帮助你更好地理解和应用所学知识。

通过以上方法,可以有效地提高算法和数据结构的学习效果,为未来的编程和开发工作打下坚实的基础。