每日一题:多米诺骨牌均匀状态 | 豆包MarsCode AI 刷题

49 阅读4分钟

问题描述

小S玩起了多米诺骨牌,他排列了一行骨牌,并可能将某些骨牌向左或向右推倒。随着骨牌连锁反应的进行,一些骨牌可能因为左右两侧受力平衡而保持竖立。现在小S想要知道在所有动作完成后,哪些骨牌保持竖立。

给定一个表示骨牌初始状态的字符串,其中:

  • "L" 表示该位置的骨牌将向左倒。
  • "R" 表示该位置的骨牌将向右倒。
  • "." 表示该位置的骨牌初始时保持竖立。

模拟整个骨牌倒下的过程,求出最终仍然保持竖立的骨牌的数目和位置。

测试样例

样例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'

方法一:广度优先搜索

思路

当时间为 0 时,部分骨牌会受到一个初始的向左或向右的力而翻倒。过了 1 秒后,这些翻倒的骨牌会对其周围的骨牌施加一个力。具体表现为:

  • 向左翻倒的骨牌,如果它有直立的左边紧邻的骨牌,则会对该直立的骨牌施加一个向左的力。
  • 向右翻倒的骨牌,如果它有直立的右边紧邻的骨牌,则会对该直立的骨牌施加一个向右的力。

接下去需要分析这些 1 秒时受力的骨牌的状态。如果仅受到单侧的力,它们会倒向单侧;如果受到两个力,则会保持平衡。再过 1秒后,这些新翻倒的骨牌又会对其他直立的骨牌施加力,而不会对正在翻倒或已经翻倒的骨牌施加力。

这样的思路类似于广度优先搜索。我们用一个队列 q 模拟搜索的顺序;数组 time 记录骨牌翻倒或者确定不翻倒的时间,翻倒的骨牌不会对正在翻倒或者已经翻倒的骨牌施加力;数组 force 记录骨牌受到的力,骨牌仅在受到单侧的力时会翻倒。

def solution(num, data):
    n = num
    q = deque()
    time = [-1] * n
    force = [[] for _ in range(n)]
    
    # 初始化队列和时间数组
    for i, f in enumerate(data):
        if f != '.':
            q.append(i)
            time[i] = 0
            force[i].append(f)
​
    res = ['.'] * n
    while q:
        i = q.popleft()
        if len(force[i]) == 1:
            res[i] = f = force[i][0]
            ni = i - 1 if f == 'L' else i + 1
            if 0 <= ni < n:
                t = time[i]
                if time[ni] == -1:
                    q.append(ni)
                    time[ni] = t + 1
                    force[ni].append(f)
                elif time[ni] == t + 1:
                    force[ni].append(f)
    
    # 记录竖立的骨牌
    standing_indices = []
    for i in range(n):
        if res[i] == '.':
            standing_indices.append(i + 1)  # 位置从1开始计数
    
    # 生成输出字符串
    if not standing_indices:
        return '0'
    else:
        return f"{len(standing_indices)}:" + ",".join(map(str, standing_indices))

方法二:模拟

思路

我们可以枚举所有连续的没有被推动的骨牌,根据这段骨牌的两边骨牌(如果有的话)的推倒方向决定这段骨牌的最终状态:

  • 如果两边的骨牌同向,那么这段连续的竖立骨牌会倒向同一方向。
  • 如果两边的骨牌相对,那么这段骨牌会向中间倒。
  • 如果两边的骨牌相反,那么这段骨牌会保持竖立。

特别地,如果左侧没有被推倒的骨牌,则假设存在一块向左倒的骨牌;如果右侧没有被推倒的骨牌,则假设存在一块向右倒的骨牌。这样的假设不会破坏骨牌的最终状态,并且边界情况也可以落入到上述三种情况中。

我们可以使用两个指针 i 和 j 来枚举所有连续的没有被推动的骨牌,left 和 right 表示两边骨牌的推倒方向。根据上述三种情况来计算骨牌的最终状态。

def solution(num, data):
    s = list(data)
    n, i, left = len(s), 0, 'L'
    while i < n:
        j = i
        while j < n and s[j] == '.':  # 找到一段连续的没有被推动的骨牌
            j += 1
        right = s[j] if j < n else 'R'
        if left == right:  # 方向相同,那么这些竖立骨牌也会倒向同一方向
            while i < j:
                s[i] = right
                i += 1
        elif left == 'R' and right == 'L':  # 方向相对,那么就从两侧向中间倒
            k = j - 1
            while i < k:
                s[i] = 'R'
                s[k] = 'L'
                i += 1
                k -= 1
        left = right
        i = j + 1
    
    # 记录竖立的骨牌
    standing_indices = []
    for i in range(n):
        if s[i] == '.':
            standing_indices.append(i + 1)  # 位置从1开始计数
    
    # 生成输出字符串
    if not standing_indices:
        return '0'
    else:
        return f"{len(standing_indices)}:" + ",".join(map(str, standing_indices))