多米诺骨牌倒下的模拟与分析
在计算机科学中,模拟现象是一个常见的任务,尤其是在物理和游戏领域。
问题描述
分析
解决思路
这道题显而易见的可以推断出重点是在标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();
代码分析
-
状态初始化: 我们首先将输入字符串转换为一个对象数组
states,每个对象包含三个属性:amo:表示该骨牌受到的力的影响,初始为0。index:骨牌在字符串中的索引。input:骨牌的初始状态('L'、'R'或'.')。
-
模拟倒下过程: 我们遍历
states数组,检查每个骨牌的状态:- 如果骨牌是 'L',则其左侧相邻骨牌的
amo值减1,表示受到向左的力。 - 如果骨牌是 'R',则其右侧相邻骨牌的
amo值加1,表示受到向右的力。 这种方式模拟了骨牌之间的相互影响。
- 如果骨牌是 'L',则其左侧相邻骨牌的
-
结果统计: 最后,我们通过过滤
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)。
结论
希望本文能够帮助读者更好地理解这一过程,并在实际项目中应用类似的思路。