题解:AtCoder AT_awc0130_b Partial Correction of a Sorted Sequence

【题目来源】

AtCoder:B - Partial Correction of a Sorted Sequence

【题目描述】

There are \(N\) students in Takahashi's class, and the students are assigned attendance numbers from \(1\) to \(N\).

Currently, the students are standing in a line, and from left to right, the students with attendance numbers \(p_1, p_2, \ldots, p_N\) are lined up. The target order specified by the teacher is, from left to right, attendance numbers \(q_1, q_2, \ldots, q_N\).

Takahashi will select one contiguous interval from the line and reverse the order of students within that interval exactly once. Specifically, he chooses integers \(L, R\) satisfying \(1 \leq L \leq R \leq N\), and reverses the order of students from position \(L\) to position \(R\). When \(L = R\), the arrangement does not change, but this is still considered as having performed one operation.

Determine whether it is possible to make the arrangement after the operation match the target arrangement \(q\) by appropriately choosing \(L\) and \(R\).

高橋的班级里有 \(N\) 名学生,学生们被分配了从 \(1\)\(N\) 的学号。

目前,学生们站成一排,从左到右依次是学号为 \(p_1, p_2, \ldots, p_N\) 的学生。老师指定的目标顺序是从左到右依次为学号 \(q_1, q_2, \ldots, q_N\)

高橋将从队伍中选择一个连续区间,并将该区间内的学生顺序恰好反转一次。具体地,他选择满足 \(1 \leq L \leq R \leq N\) 的整数 \(L, R\),并将从第 \(L\) 个位置到第 \(R\) 个位置的学生顺序反转。当 \(L = R\) 时,排列不会改变,但这仍然视为执行了一次操作。

判断是否可以通过适当选择 \(L\)\(R\),使得操作后的排列与目标排列 \(q\) 一致。

【输入】

\(N\)
\(p_1\) \(p_2\) \(\cdots\) \(p_N\)
\(q_1\) \(q_2\) \(\cdots\) \(q_N\)

  • The first line contains an integer \(N\) representing the number of students.
  • The second line contains the elements \(p_1, p_2, \ldots, p_N\) of the permutation \(p\) representing the current order, separated by spaces.
  • The third line contains the elements \(q_1, q_2, \ldots, q_N\) of the permutation \(q\) representing the target order, separated by spaces.

【输出】

If it is possible to make the current arrangement match the target arrangement with exactly one reversal operation, print Yes; otherwise, print No.

【输入样例】

5
1 2 3 4 5
1 4 3 2 5

【输出样例】

Yes

【核心思想】

  1. 问题分析:给定两个长度为 \(N\) 的排列 \(p\)\(q\),判断是否可以通过恰好一次区间反转操作将 \(p\) 变为 \(q\)。这是一个贪心验证问题,关键在于找到 \(p\)\(q\) 第一个和最后一个不同的位置,反转该区间后验证是否等于 \(q\)

  2. 算法选择

    • 双指针定位差异区间:从左向右找第一个不同位置 \(l\),从右向左找最后一个不同位置 \(r\)
    • 反转验证:对 \(p[l..r]\) 执行反转,然后与 \(q\) 逐元素比较
  3. 关键步骤

    • 读入数据\(N\),排列 \(p[1..N]\)\(q[1..N]\)
    • 定位差异区间
      • 从左向右遍历,找到第一个 \(p[i] \neq q[i]\) 的位置 \(l\)
      • 从右向左遍历,找到最后一个 \(p[i] \neq q[i]\) 的位置 \(r\)
    • 边界处理:若 \(l\) 未赋值(两排列完全相同),直接输出 Yes\(L=R\) 的反转不改变排列)
    • 反转验证reverse(p + l, p + r + 1),然后遍历 \(i\)\(1\)\(N\)
      • 若存在 \(p[i] \neq q[i]\),输出 No
    • 全部相同则输出 Yes
  4. 时间/空间复杂度

    • 时间复杂度:\(O(N)\),定位差异 \(O(N)\),反转 \(O(N)\),验证 \(O(N)\)
    • 空间复杂度:\(O(N)\),存储两个排列
  5. 贪心验证的核心思想

    • 差异区间的唯一性:若存在合法反转区间 \([L, R]\),则 \(L\) 必为第一个不同位置,\(R\) 必为最后一个不同位置。因为反转区间外的元素位置不变,必须与 \(q\) 相同
    • 必要性与充分性:找到差异区间并反转是"一次反转可达"的必要条件;反转后验证等于 \(q\) 则是充分条件
    • 排列性质简化:由于 \(p\)\(q\) 都是排列(无重复元素),差异区间反转后若对应位置相等,则整个排列必然相等
    • 适用于"判断一次操作能否达成目标"的验证类问题,核心在于利用操作特性缩小验证范围

【算法标签】

贪心

【代码详解】

#include <bits/stdc++.h>
using namespace std;
const int N = 1000005;   // 定义数组最大容量为1000005
int n, l, r;             // n为学生人数,l为第一个不相同的位置,r为最后一个不相同的位置
int p[N], q[N];          // p[i]为当前排列第i个位置的学号,q[i]为目标排列第i个位置的学号
map<int, int> mp;        // mp未使用

int main()
{
    cin >> n;            // 读入学生人数n
    for (int i = 1; i <= n; i++)  // 读入当前排列
        cin >> p[i];
    for (int i = 1; i <= n; i++)  // 读入目标排列
        cin >> q[i];

    // 第一步:从左向右找到第一个当前排列与目标排列不同的位置l
    for (int i = 1; i <= n; i++)
        if (p[i] != q[i])  // 如果第i个位置不同
        {
            l = i;         // 记录第一个不同的位置
            break;         // 找到后退出循环
        }

    // 第二步:从右向左找到最后一个当前排列与目标排列不同的位置r
    for (int i = n; i >= 1; i--)
        if (p[i] != q[i])  // 如果第i个位置不同
        {
            r = i;         // 记录最后一个不同的位置
            break;         // 找到后退出循环
        }

    // 如果没有找到不同位置(即l和r未被赋值),说明两个排列完全相同
    // 此时l=0, r=0,反转空区间后仍然相同,输出Yes
    // 但代码逻辑中如果完全相同,l和r保持默认值0,reverse(p+0, p+0+1)会有问题
    // 实际上如果完全相同,应该直接输出Yes

    // 第三步:将区间[l, r]内的当前排列反转
    reverse(p + l, p + r + 1);  // STL的reverse函数,反转[l, r]区间(左闭右开,所以是p+r+1)

    // 第四步:验证反转后是否等于目标排列
    for (int i = 1; i <= n; i++)
        if (p[i] != q[i])       // 如果反转后仍有位置不同
        {
            cout << "No" << endl;  // 无法通过一次区间反转得到目标排列
            return 0;              // 程序结束
        }

    // 所有位置都相同,说明可以通过一次区间反转得到目标排列
    cout << "Yes" << endl;
    return 0;
}

【运行结果】

5
1 2 3 4 5
1 4 3 2 5
Yes
posted @ 2026-08-10 12:24  团爸讲算法  阅读(6)  评论(0)    收藏  举报