洛谷luogu题解:P14330 [JOI2021 预选赛 R2] 往返滑道 / Round Sugoroku

原题传送门

题意简述

滑道长度为 (N+2),编号从 (0) 到 (N+1),其中:

  • (S_0 = S_{N+1} = \texttt{x}):两端为墙。
  • (S_1 \sim S_N):可能是字母、.#
  • 棋子初始在位置 (A),方向向右,且 (S_A) 是字母。

每秒棋子向当前方向移动一格,规则如下:

  • 落在 x:反转方向。
  • 落在 #:反转方向 + 变为 .(只触发一次)。
  • 落在 .:无变化。

目标:清除所有 # 所需的时间。


解法思路

核心观察

每个 # 只能清除一次,且每次清除都会反转方向。棋子在滑道上来回穿梭,形成“往返”路径。

分治策略

将所有 # 分为左右两部分:

  • 左侧:位置 (< A)
  • 右侧:位置 (> A)

每次向当前方向移动到最近的 #,清除后反转方向。若当前方向没有 #,则先移动到端点,再转向另一侧。


简洁代码(码风优良)

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);

    int n, a;
    string s;
    cin >> n >> a >> s;

    vector<int> l, r;
    for (int i = 0; i < n; i++)
        if (s[i] == '#')
            (i + 1 < a ? l : r).push_back(i + 1);

    sort(l.rbegin(), l.rend());
    sort(r.begin(), r.end());

    int p = a, d = 1;
    long long t = 0;

    while (!l.empty() || !r.empty()) {
        if (d == 1) {
            if (!r.empty()) {
                t += r[0] - p;
                p = r[0];
                r.erase(r.begin());
            } else {
                t += (n + 1 - p) + (l.empty() ? 0 : n + 1 - l[0]);
                p = l.empty() ? 0 : l[0];
                d = -1;
                continue;
            }
        } else {
            if (!l.empty()) {
                t += p - l[0];
                p = l[0];
                l.erase(l.begin());
            } else {
                t += p + (r.empty() ? 0 : r[0]);
                p = r.empty() ? n + 1 : r[0];
                d = 1;
                continue;
            }
        }
        d *= -1;
    }

    cout << t << '\n';
}

时间复杂度

  • 初始遍历:(O(N))
  • 每次清除一个 #:最多 (O(K)),其中 (K) 是 # 的数量
  • 总复杂度:(O(N + K)),线性级别

总结

这题的关键在于:

  • 每次只处理一个 #,清除后反转方向。
  • 当前方向没有 # 时,合理计算到端点再转向的时间。
  • 用两个有序数组维护左右 #,避免逐步模拟。

后记

我写了暴力、链表、数学方法以后,终于作出了正解。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值