多米诺骨牌均衡状态 | 豆包MarsCode AI刷题

95 阅读4分钟

具体描述

image.png

代码

#include <bits/stdc++.h>
using namespace std;

string solution(int num, string data) {
    vector<int> positions;
    int sum = 0;

    vector<int> index;
    for (int i = 0; i < num; i++) {
        if (data[i] != '.') index.push_back(i);
    }

    if (index.empty()) {
        ostringstream ans;
        ans << num << ":";
        for (int i = 1; i <= num; i++) {
            if (i > 1) ans << ",";
            ans << i;
        }
        return ans.str();
    }

    for (int i = 1; i < index.size(); i++) {
        int left = index[i - 1], right = index[i];
        if (data[left] == 'L' && data[right] == 'R') {  // LR case: All between are balanced.
            for (int j = left + 1; j < right; j++) positions.push_back(j + 1);
            sum += right - left - 1;
        } else if (data[left] == 'R' && data[right] == 'L' && (left + right) % 2 == 0) {  // RL case: Only one balanced.
            positions.push_back((left + right) / 2 + 1);
            sum++;
        }
    }

    if (data[index[0]] == 'R') {
        for (int i = 0; i < index[0]; i++) positions.push_back(i + 1);
        sum += index[0];
    }
    if (data[index.back()] == 'L') {
        for (int i = index.back() + 1; i < num; i++) positions.push_back(i + 1);
        sum += num - 1 - index.back();
    }

    sort(positions.begin(), positions.end());
    ostringstream ans;
    ans << sum;
    if (!positions.empty()) {
        ans << ":";
        for (size_t i = 0; i < positions.size(); i++) {
            if (i > 0) ans << ",";
            ans << positions[i];
        }
    }
    return ans.str();
}

int main() {
    cout << (solution(14, ".L.R...LR..L..") == "4:3,6,13,14") << endl;
    cout << (solution(5, "R....") == "0") << endl;
    cout << (solution(1, ".") == "1:1") << endl;
    
    return 0;
}

思路

这个问题的核心是模拟多米诺骨牌的倒下过程,并确定哪些骨牌最终保持竖立。初始状态包括三种情况:

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

我们需要分析骨牌倒下的相互作用,尤其是如何处理骨牌之间的力平衡。目标是确定最终状态中保持竖立的骨牌位置,并输出它们的总数和具体位置。

问题分解

  • 找到所有的 "L" 和 "R" 的位置
    这些位置是力的来源。我们遍历整个字符串,记录所有非 "." 的位置索引。

  • 处理特殊情况

    • 如果整个字符串都是 "."(没有 "L" 或 "R"),则所有骨牌都保持竖立。
    • 如果字符串中没有任何力作用的骨牌,输出所有位置并终止。
  • 分析相邻的 "L" 和 "R" 的关系

    • LR
      如果相邻的两个骨牌分别是 "L" 和 "R",它们之间的骨牌将全部保持竖立。

    • RL
      如果相邻的两个骨牌是 "R" 和 "L",需要判断它们之间的骨牌数是否为偶数:

      • 偶数:中间的一个骨牌会保持竖立,因为两边的力平衡。
      • 奇数:所有骨牌都会倒下,没有骨牌竖立。
    • RR / LL
      同向的骨牌之间不会有保持竖立的骨牌。

  • 处理头部和尾部

    • 头部:"R" 出现在最左边时,它左边的所有骨牌都会被推倒。
    • 尾部:"L" 出现在最右边时,它右边的所有骨牌都会被推倒。
  • 输出处理
    将所有保持竖立的骨牌位置排序,并构建输出字符串。

解法分析

主要步骤

  1. 遍历字符串,记录所有非 "." 的索引。

  2. 根据索引之间的关系处理骨牌竖立情况:

    • 处理 LRRL 情况。
    • 处理头部和尾部的特殊情况。
  3. 构建最终输出字符串。

分步解析

vector<int> index;
for (int i = 0; i < num; i++) {
    if (data[i] != '.') index.push_back(i);
}
  • 作用:记录所有非 "." 的索引。
if (index.empty()) {
    ostringstream ans;
    ans << num << ":";
    for (int i = 1; i <= num; i++) {
        if (i > 1) ans << ",";
        ans << i;
    }
    return ans.str();
}
  • 作用:处理全是 "." 的情况。所有骨牌都保持竖立。
for (int i = 1; i < index.size(); i++) {
    int left = index[i - 1], right = index[i];
    if (data[left] == 'L' && data[right] == 'R') {
        // LR: All between remain upright
    } else if (data[left] == 'R' && data[right] == 'L' && (left + right) % 2 == 0) {
        // RL: Only one in the middle remains upright (if distance is even)
    }
}
  • 作用:核心逻辑,分析不同的骨牌对之间的力作用。
ostringstream ans;
ans << sum;
if (!positions.empty()) {
    ans << ":";
    for (size_t i = 0; i < positions.size(); i++) {
        if (i > 0) ans << ",";
        ans << positions[i];
    }
}
  • 作用:将结果拼接成所需格式。

时空复杂度

  • 时间复杂度

    • 遍历字符串一次:O(n)O(n)O(n)。
    • 对索引向量进行排序:O(klog⁡k)O(k \log k)O(klogk),其中 kkk 是竖立骨牌的数量。
    • 总时间复杂度:O(n+klog⁡k)O(n + k \log k)O(n+klogk)。由于 k≤nk \leq nk≤n,整体复杂度为 O(nlog⁡n)O(n \log n)O(nlogn)。
  • 空间复杂度

    • 额外使用了两个向量来存储索引和保持竖立的骨牌位置:O(n)O(n)O(n)。

总结

这道题本质上是一个模拟过程,要求我们根据骨牌的倒下状态("L" 和 "R")来推导出最终的竖立状态。在模拟过程中,关键在于如何处理力的传播以及力的平衡。

处理问题时,边界条件的正确性往往是影响结果的关键:

  • 字符串全是 "." 时,所有骨牌都应保持竖立。
  • 头部尾部 的特殊情况:若头部有 "R",则左边所有骨牌都会被推倒;若尾部有 "L",则右边所有骨牌都会被推倒。

这些边界条件一旦忽略,就会导致最终结果错误。通过细致的处理这些边界条件,我深刻体会到在解决实际问题时,如何在细节上做到严谨。