1 条题解
-
0
题目理解
我们有一个长度为 的路径,单元格可能是:
'.':空'@':硬币'*':荆棘
起点是第 个单元格(下标从 开始,代码中从 开始),保证是
'.'。移动规则:
- 每次可以走 步 或 步
- 目标单元格 不能是荆棘
'*' - 走到硬币单元格自动捡起硬币
目标:从起点出发,按规则移动,最多能捡多少硬币。
核心思路
标程给出的思路非常巧妙,不需要复杂 DP:
尽量走 步,如果下一步是荆棘,就走 步;如果连续两步都是荆棘,则游戏结束。
为什么这样是对的?
- 如果下一步
s[i]不是荆棘,走 步是最优的,因为:- 不会错过硬币
- 不会浪费移动机会
- 如果下一步
s[i]是荆棘,必须走 步(前提是 不是荆棘)。 - 如果当前格子的下一步
s[i]是荆棘,并且下两步s[i+1]也是荆棘,那么无论走 步还是 步都会踩到荆棘,游戏终止。
因此,游戏会在第一次遇到连续两个荆棘
"**"时被迫结束。
算法步骤
- 初始化 。
- 从 到 遍历字符串(跳过起点 ):
- 如果
s[i] == '@', 加 。 - 如果
s[i] == '*' && s[i-1] == '*',立即 结束循环。
- 如果
- 输出 。
为什么不需要显式模拟跳跃?
标程巧妙地没有模拟跳跃,而是直接遍历并统计硬币,直到遇到
"**"。为什么这样可行?
- 因为只要没遇到
"**",我们 总是有办法 走到当前位置。 - 我们走过的路径是:
- 如果遇到荆棘,就跳 步,此时当前遍历的
i可能跳过一些格子,但硬币仍然被统计。 - 实际上,遍历顺序不是实际跳跃顺序,但统计结果是等价的。
- 如果遇到荆棘,就跳 步,此时当前遍历的
更严格地说:
从起点开始,实际跳跃路径中能到达的格子,就是 从位置 开始,遇到"**"之前的所有'@'位置,因为:- 只要不是两个荆棘连续,总可以通过适当选择 步或 步跳过去。
- 遇到
"**"就无法继续。
因此,只需要统计 第一个
"**"之前的所有'@'。
时间复杂度
- 每个测试用例:
- ,,完全可行。
代码对应关系
for (int i = 1; i < n; i++) { ans += (s[i] == '@'); if (s[i] == '*' && s[i - 1] == '*') break; }i = 1开始:从第二个格子开始判断ans += (s[i] == '@'):遇到硬币就计数if (s[i] == '*' && s[i-1] == '*') break;:遇到"**"结束
示例验证
示例 1
.@@*@.**@@遍历:
- i=1:
@→ ans=1 - i=2:
@→ ans=2 - i=3:
*→ 不计数,且 s[2]='@', s[3]='*' 不是连续** - i=4:
@→ ans=3 - i=5:
.→ ans=3 - i=6:
*→ 不计数,且 s[5]='.', s[6]='*' 不是** - i=7:
*→ 不计数,且 s[6]='', s[7]='' 是**→ break 输出 ✅
示例 2
.@@@@i=1..4 全是
@,无**→ ans=4 ✅示例 3
.@@..@***..@@@*i=1:
@→ 1
i=2:@→ 2
i=3:.→ 2
i=4:.→ 2
i=5:@→ 3
i=6:*→ 3,且 s[5]='@', s[6]='' 不是**
i=7:*→ 3,且 s[6]='', s[7]='*' 是**→ break
输出 ✅
总结
项目 说明 核心观察 遇到连续两个荆棘 "**"就无法继续算法 统计第一个 "**"之前的所有'@'复杂度 每个测试用例 难度 CF 800 分 / 1–10 分制下约 1.5 #include <bits/stdc++.h> using namespace std; signed main() { cin.tie(nullptr); cout.tie(nullptr); ios::sync_with_stdio(false); int t; cin >> t; for(int _ = 0; _ < t; ++_){ int n, ans = 0; cin >> n; string s; cin >> s; for (int i = 1; i < n; i++) { ans += (s[i] == '@'); if (s[i] == '*' && s[i - 1] == '*') break; } cout << ans << "\n"; } return 0; }
- 1
信息
- ID
- 6428
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 1
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者