多米诺骨牌模拟 | 豆包MarsCode AI 刷题

80 阅读4分钟

多米诺骨牌倒下的模拟与分析

在计算机科学中,模拟现象是一个常见的任务,尤其是在物理和游戏领域。

问题描述

image.png

分析

解决思路

这道题显而易见的可以推断出重点是在标L R的牌上,但不仅仅限于此。因为当“.”牌左边或右边有倾倒的牌,他也会被“感染”为L R牌。

所以重点是模拟计数左右两侧的牌型。为了省时间和空间考虑,直接做计数。

很简单,当左侧有牌时,当前牌 为右倾,给计数器加一。 当右侧有牌时,当前牌 为左倾,给计数器减一。

最后我们只需要统计计数器为0的牌即可。

抵消

你可以简单的理解为:如果一个骨牌左边和右边都被推倒了相同次数,那么它们就会相互抵消,骨牌就不会倒下。就像是左边有人推你一下,右边也有人拉你一下,如果你力气够大,你就不会倒。

次数则是:我们数次数是因为,如果一个骨牌被推倒了奇数次,那么它最后就会倒下;如果被推倒了偶数次,它就会保持竖直。这就像是如果你被推了奇数次,你可能会失去平衡;但如果被推了偶数次,你就能保持平衡。

循环

显而易见的可以考虑到循环,你可能会确保无误采用2次循环。其实非常简单,我们只需要循环一次即可。

重点就在于如何循环。

我们假定遍历的牌为i,当i为L时,我们只需要将他左侧的牌计数,当i为R时,我们只需要将他右侧的牌计数,最后去掉边界情况即可。

  // 先找到states中所有等于L或者R的 影响相邻位置
  for (let i = 0; i < states.length; ++i) {
    const item = states[i];

    if (item.input === 'L') {
      if (i <= 0) continue;
      const prevItem = states[i - 1];
      prevItem.amo -= 1; // 左侧骨牌受力
    } else if (item.input === 'R') {
      if (i >= states.length - 1) continue;
      const nextItem = states[i + 1];
      nextItem.amo += 1; // 右侧骨牌受力
    }
  }

这就是我们所采用的相邻影响法,这样在一次循环后,所有的牌状态全部确定。

代码实现

下面是实现上述逻辑的 JavaScript 代码:

function solution(input) {
  const states = [...input].map((i, index) => ({
    amo: 0,
    index,
    input: i,
  }));

  // 先找到states中所有等于L或者R的 影响相邻位置
  for (let i = 0; i < states.length; ++i) {
    const item = states[i];

    if (item.input === 'L') {
      if (i <= 0) continue;
      const prevItem = states[i - 1];
      prevItem.amo -= 1; // 左侧骨牌受力
    } else if (item.input === 'R') {
      if (i >= states.length - 1) continue;
      const nextItem = states[i + 1];
      nextItem.amo += 1; // 右侧骨牌受力
    }
  }

  const result = states.filter(i => i.amo === 0 && input[i.index] === '.');

  console.log(result.length);

  if (result.length) {
    console.log(result.map(i => i.index + 1).join(' '));
  }
}

function main() {
  solution(".L.R...LR..L..");
}

main();

代码分析

  1. 状态初始化: 我们首先将输入字符串转换为一个对象数组 states,每个对象包含三个属性:

    • amo:表示该骨牌受到的力的影响,初始为0。
    • index:骨牌在字符串中的索引。
    • input:骨牌的初始状态('L'、'R'或'.')。
  2. 模拟倒下过程: 我们遍历 states 数组,检查每个骨牌的状态:

    • 如果骨牌是 'L',则其左侧相邻骨牌的 amo 值减1,表示受到向左的力。
    • 如果骨牌是 'R',则其右侧相邻骨牌的 amo 值加1,表示受到向右的力。 这种方式模拟了骨牌之间的相互影响。
  3. 结果统计: 最后,我们通过过滤 states 数组,找出 amo 值为0且状态为 '.' 的骨牌。这些骨牌就是最终保持竖立的骨牌。我们输出它们的数量和位置。

测试样例

我们可以使用以下测试样例来验证我们的实现:

  • 样例1: 输入:num = 14, data = ".L.R...LR..L.."
    输出:4:3,6,13,14

  • 样例2: 输入:num = 5, data = "R...."
    输出:0

  • 样例3: 输入:num = 1, data = "."
    输出:1:1

复杂度分析

该算法的时间复杂度为 O(n),其中 n 是输入字符串的长度。我们只需遍历字符串两次:一次用于初始化状态,另一次用于更新状态并统计结果。因此,该算法在处理大规模输入时仍然高效。

然而一般在初始化遍历中其实并不算入计数,不过无论如何我们的算法复杂度都是O(n)。

结论

希望本文能够帮助读者更好地理解这一过程,并在实际项目中应用类似的思路。