题解:学而思编程 蚂蚁碰撞
【题目来源】
学而思编程:蚂蚁碰撞
【题目描述】
在一个无限长的木板上有 \(n\) 只蚂蚁正在移动。蚂蚁按照从左到右的顺序从 \(1\) 到 \(n\) 编号,每只蚂蚁都在一个与其他蚂蚁互不相同的位置上。
初始时,每只蚂蚁都有一个初始朝向:\(L\) 表示向左移动,\(R\) 表示向右移动,\(S\) 表示静止不动。每只蚂蚁的移动时的速度相同,然而在移动过程中部分蚂蚁之间会产生碰撞,在两只蚂蚁发生碰撞之后,都会停留在碰撞的位置无法再继续移动。所有蚂蚁不能改变它们的移动方向和状态。
现在给定所有蚂蚁的初始朝向,请你计算所有蚂蚁在木板上发生的碰撞总次数。碰撞次数按照一下方式计算:
- 当两只移动方向相反的蚂蚁相撞时,碰撞次数加 \(2\);
- 当一只移动的蚂蚁和一只静止的蚂蚁相撞时,碰撞次数加 \(1\)。
【输入】
一行一个字符串 \(dir\),表示所有蚂蚁的初始朝向,保证 \(dir\) 只包含 \(L\),\(R\) 或 \(S\)。
【输出】
一行一个整数,表示所有蚂蚁在木板上发生的碰撞总次数。
【输入样例】
RLRSLL
【输出样例】
5
【核心思想】
-
问题分析:给定 \(n\) 只蚂蚁的初始朝向序列(
L向左、R向右、S静止),蚂蚁速度相同且碰撞后停止。碰撞规则为:相向碰撞(R与L)次数加 \(2\),移动与静止碰撞(R/L与S)次数加 \(1\)。求总碰撞次数。这是一个字符串分析 + 贪心边界裁剪问题,关键在于识别有效碰撞区域并统计其中的移动蚂蚁数量。 -
算法选择:
- 边界裁剪:去掉前缀所有
L(这些蚂蚁向左走,左侧无蚂蚁,不会碰撞)和后缀所有R(这些蚂蚁向右走,右侧无蚂蚁,不会碰撞) - 有效区域统计:裁剪后的中间区域内,所有非静止蚂蚁
R或L必然参与至少一次碰撞
- 边界裁剪:去掉前缀所有
-
关键步骤:
- 读取朝向字符串
dir - 左边界裁剪:
- 指针
l从 \(0\) 开始右移,跳过所有前缀L - 这些
L向左移动,左侧无蚂蚁,不会与任何蚂蚁碰撞
- 指针
- 右边界裁剪:
- 指针
r从末尾开始左移,跳过所有后缀R - 这些
R向右移动,右侧无蚂蚁,不会与任何蚂蚁碰撞
- 指针
- 统计有效碰撞次数:
- 遍历裁剪后的区间 \([l, r]\):
- 若
dir[i] != 'S'(即R或L):ans++
- 若
- 返回
ans
- 遍历裁剪后的区间 \([l, r]\):
- 读取朝向字符串
-
时间/空间复杂度:
- 时间复杂度:\(O(n)\),两次边界扫描 \(O(n)\),中间统计 \(O(n)\)
- 空间复杂度:\(O(1)\),仅需常数额外空间
-
边界裁剪的核心思想:
- 无效蚂蚁识别:前缀
L和后缀R是"逃逸蚂蚁",它们朝远离其他蚂蚁的方向移动,永远不会参与碰撞,可以直接排除 - 有效区域性质:裁剪后的中间区域内,任何
R右侧必然存在L或S,任何L左侧必然存在R或S,因此每个移动蚂蚁至少会发生一次碰撞 - 碰撞规则统一:在有效区域内,每个非静止蚂蚁贡献恰好 \(1\) 次碰撞计数(相向碰撞被拆分为两只蚂蚁各贡献 \(1\),移动与静止碰撞也是 \(1\)),因此总次数等于有效区域内移动蚂蚁的数量
- 贪心最优性:边界裁剪是确定性的,不存在遗漏或重复计算,每个有效区域内的移动蚂蚁必然且仅贡献一次碰撞
- 适用于"一维相向运动 + 碰撞停止"类问题,边界逃逸分析可将复杂模拟转化为简单字符串处理
- 无效蚂蚁识别:前缀
【算法标签】
字符串入门
【代码详解】
#include <bits/stdc++.h>
using namespace std;
int cal(string s)
{
int l = 0, r = s.size() - 1; // 左右指针
// 去掉前缀的'L'
while (l <= r && s[l] == 'L')
{
l++;
}
// 去掉后缀的'R'
while (l <= r && s[r] == 'R')
{
r--;
}
int ans = 0;
// 统计中间非'S'字符的个数
for (int i = l; i <= r; i++)
{
if (s[i] != 'S')
{
ans++;
}
}
return ans;
}
int main()
{
string dir;
cin >> dir; // 输入字符串
cout << cal(dir); // 输出计算结果
return 0;
}
【运行结果】
RLRSLL
5
浙公网安备 33010602011771号