问题描述
小 A 将 n 个多米诺骨牌放在一条线上,每一块都垂直竖立。他同时将一些骨牌向左或向右推倒。注意:不会出现连续向左或者向右推的情况。 每过一秒,被推向左边或右边的骨牌会将左边或右边的相邻骨牌推倒。当一个骨牌,其左边倒向它的骨牌数目与其右边倒向它的骨牌数目相等时,由于力的平衡,该骨牌将依然保持竖立。给定小 A 最初推骨牌的方向,求出最后依然保持竖立的骨牌数目和位置。
问题分析
给定一组多米诺骨牌,初始时有些骨牌被推向左('L')或右('R'),而其他骨牌处于竖立状态('.')。目标是模拟多米诺骨牌倒下的过程,最终返回所有仍然竖立的骨牌的位置。需要考虑到多米诺骨牌的传递效应:当一个骨牌被推倒时,它会推倒相邻的骨牌,直到没有更多骨牌会被推倒为止。
可以通过以下几个步骤来解决这个问题:
-
初始化和变量定义:
- 使用一个变量
right来记录最近的R的位置,它表示推向右的骨牌的起始点。 - 使用
dominoes字符串来表示多米诺骨牌的状态,R表示右推,L表示左推,.表示竖立状态。 standing用于记录最终竖立的骨牌位置。
- 使用一个变量
-
遍历字符串处理推倒的骨牌:(重点:跟踪当前位置骨牌的状态)
- 如果遇到
R(向右推的骨牌),就更新right为当前位置。 - 如果遇到
L(向左推的骨牌),就检查之前是否有R出现过(即是否有骨牌正在向右推倒)。- 如果有
R,则从R到L之间的所有竖立骨牌会平衡地倒下(即左边逐渐倒向R,右边逐渐倒向L)。中间的骨牌会左右倒向,相等时保持竖立。 - 如果没有
R,则只能让所有竖立的骨牌向左倒(即从当前位置向左推)。
- 如果有
- 如果遇到
-
处理最后一段
R...的情况:- 如果最后还有
R(即没有L在其后),那么所有R后面的竖立骨牌都会被推倒。
- 如果最后还有
-
统计最终竖立的骨牌:
- 在整个过程结束后,遍历
dominoes字符串,找出仍然竖立的骨牌(即状态为.的骨牌),记录它们的位置。
- 在整个过程结束后,遍历
代码:
std::string solution(int n, std::string dominoes) {
std::vector<int> standing;
int right = -1; // 记录最后一次出现的 R 的位置
for (int i = 0; i < n; ++i) {
if (dominoes[i] == 'R') {
right = i; // 记录向右推的位置
} else if (dominoes[i] == 'L') {
if (right != -1) {
// 处理 R...L 之间的骨牌
int start = right + 1;
int end = i - 1;
while (start < end) {
dominoes[start++] = 'R';
dominoes[end--] = 'L';
}
right = -1; // 处理完后清除 R 的状态
} else {
// 没有 R,只能从当前位置左推到最远
for (int j = i - 1; j >= 0 && dominoes[j] == '.'; --j) {
dominoes[j] = 'L';
}
}
}
}
// 处理最后一段 R... 的情况,这些骨牌都会被推倒
if (right != -1) {
for (int i = right + 1; i < n && dominoes[i] == '.'; ++i) {
dominoes[i] = 'R';
}
}
// 再次遍历,统计竖立的骨牌
for (int i = 0; i < n; ++i) {
if (dominoes[i] == '.') {
standing.push_back(i + 1); // 记录竖立的骨牌位置
}
}
// 返回结果
if (standing.empty()) {
return "0";
} else {
std::string result = std::to_string(standing.size()) + ":";
for (int i = 0; i < standing.size(); ++i) {
result += std::to_string(standing[i]);
if (i != standing.size() - 1) {
result += ",";
}
}
return result;
}
}
总结
这道题的重点是关注跟踪当前位置骨牌的状态,通过一次遍历所有动作处理多米诺骨牌的倒下过程,一次遍历中就考虑完了所有的平衡条件。