T1:小猴上学

本题难度简单,考察代码熟练度。对于第 \(i\) 站来说,如果第 \(i\) 站有人不能上车,就记录 \(i\) 到数组中,最终如果数组为空说明到达学校的这段时间每一站等待的人都能上车,否则说明有部分站有人不能上车。

num 记录到达第 \(i\) 站还未上下车时车上的人数

\( num = \min(num-a_i+b_i, m) \)

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)

using namespace std;

int main() {
    int n, m;
    cin >> n >> m;
    
    int num = 0;
    set<int> st;
    rep(i, n) {
        int a, b;
        cin >> a >> b;
        if (num-a+b > m) st.insert(i);
        num = min(num-a+b, m);
    }
    
    if (!st.size()) {
        puts("Yes");
        cout << num << '\n';
    }
    else {
        puts("No");
        for (int i : st) {
            cout << i+1 << '\n';
        }
    }
    
    return 0;
}

T2:数组分界

本题难度中等,考察枚举以及前缀和技巧。
枚举分界点 \(i\)\(i\) 左边的所有元素都要小于 \(0\)\(i\) 右边的所有元素都要大于 \(0\),此时统计需要修改的元素个数,\(i\) 左边的元素只要大于等于 \(0\) 就需要进行修改,\(i\) 右边的元素只要小于等于 \(0\) 就需要修改。特别注意,如果分界点 \(i\) 上的数字是 \(0\) 的话,也需要修改一次。
优化:可以利用前缀和技巧预处理,前 \(i\) 个元素和大于等于 \(0\) 的元素个数,后 \(i\) 个元素中小于等于 \(0\) 的个数,这样就可以把内层循环砍掉。
时间复杂度:\(O(n)\)

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)

using namespace std;

const int MX = 200005;

int a[MX], l[MX], r[MX];

int main() {
    int n;
    cin >> n;
    
    rep(i, n) cin >> a[i];
    
    rep(i, n) l[i] = l[i-1] + (a[i]>=0);
    for (int i = n; i >= 1; --i) r[i] = r[i+1] + (a[i]<=0);
    
    int ans = n;
    rep(i, n) {
        ans = min(ans, l[i-1 ] + r[i+1] + (a[i] == 0));
    }
    
    cout << ans << '\n';
    
    return 0;
}

T3:文本编辑器

本题难度较大,考察字符串模拟问题和栈应用。

\(60\) 分做法:

使用字符串数组 s[] 来模拟整个过程,初始时,文本内容长度 len=0 表示文本内容为空,光标 pos=0

如果是插入操作,先将 pos~len-1 位置上的元素后移一位,然后将元素 \(x\) 存到 \(pos\) 位置上,再将 \(pos\)\(len\) 加一;

如果是删除操作,如果 \(pos\) 右边没有任何内容,即 pos=len,忽略本次操作,否则将 pos+1~len-1 位置上的元素统一左移一位,再将 \(len\) 减一;

如果是前进操作,如果 \(pos\) 右边没有任何内容,即 pos=len,忽略本次操作,否则 \(pos\) 加一;

如果是后退操作,如果 \(pos\) 左边没有任何内容,即 pos=0,忽略本次操作,否则 pos 减一。

最终文本内容为字符串 \(s\) 的内容。
时间复杂度:\(O(n^2)\)

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)

using namespace std;

int n, pos, len;
char op, x, s[500010];

int main() {
    cin >> n;
    
    while (n--) {
        cin >> op;
        if (op == 'I') {
            cin >> x;
            for (int i = len-1; i >= pos; --i) s[i+1] = s[i];
            s[pos] = x;
            pos++; len++;
        }
        else if (op == 'D') {
            if (pos != len) {
                for (int i = pos+1; i <= len-1; ++i) s[i-1] = s[i];
                len--;
            }
        }
        else if (op == 'R') {
            if (pos != len) pos++;
        }
        else {
            if (pos != 0) pos--;
        }
    }
    
    rep(i, len) cout << s[i];
    
    return 0;
}

\(100\) 分做法:

分别使用两个栈来存储光标左右两侧的内容(假设存储光标左右两边内容的栈分别为 \(a, b\)):

如果是插入操作,直接将元素放入栈 \(a\) 中;
如果是删除操作,当 \(b\) 不为空时,将栈顶元素出栈;
如果是前进操作,光标右移等价于将栈 \(b\) 中的栈顶元素出栈放入栈 \(a\) 中(栈 \(b\) 不为空);
如果是后退操作,光标左移等价于将栈 \(a\) 中的栈顶元素出栈放入栈 \(b\) 中(栈 \(a\) 不为空)。

最终的文本内容为栈 \(a\) 中从栈底栈栈顶这些元素和栈 \(b\) 中从栈顶到栈底的元素依次按照顺序排列。
时间复杂度:\(O(n)\)

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)

using namespace std;

int n;
char op, x;

int main() {
    cin >> n;
    
    stack<char> a, b;
    while (n--) {
        cin >> op;
        if (op == 'I') {
            cin >> x;
            a.push(x);
        }
        else if (op == 'D') {
            if (b.size()) b.pop();
        }
        else if (op == 'R') {
            if (b.size()) {
                a.push(b.top());
                b.pop();
            }
        }
        else {
            if (a.size()) {
                b.push(a.top());
                a.pop();
            }
        }
    }
    
    string ans;
    while (a.size()) {
        ans += a.top();
        a.pop();
    }
    reverse(ans.begin(), ans.end());
    while (b.size()) {
        ans += b.top();
        b.pop();
    }
    cout << ans << '\n';
    
    return 0;
}