专栏文章

题解:AT_agc058_a [AGC058A] Make it Zigzag

AT_agc058_a题解参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@mipy7t8p
此快照首次捕获于
2025/12/03 19:53
3 个月前
此快照最后确认于
2025/12/03 19:53
3 个月前
查看原文

题目传送门

证明

为了证明这个问题的解法,我们需要说明通过一系列交换操作,可以将任意排列 PP 转换为满足“Zigzag”条件的排列,且操作次数不超过 NN 次。

结论

对于任意排列 P=(P1,P2,,P2N)P = (P_1, P_2, \cdots, P_{2N}),存在一种交换相邻元素的操作序列,使得最终排列满足以下条件:
  1. 对于每个奇数位置 i=1,3,5,,2N1i = 1, 3, 5, \cdots, 2N-1,有 Pi<Pi+1P_i < P_{i+1}
  2. 对于每个偶数位置 i=2,4,6,,2N2i = 2, 4, 6, \cdots, 2N-2,有 Pi>Pi+1P_i > P_{i+1}
并且,操作次数 KK 满足 0KN0 \leq K \leq N

证明

问题分析
  • 我们需要将排列 PP 转换为“Zigzag”形式,即奇数位置的值小于其下一个值,偶数位置的值大于其下一个值。
  • 每次操作可以交换相邻的两个元素,因此我们可以通过局部调整来满足条件。
构造性证明
  • 我们从左到右依次检查每个位置 ii
    • 如果 ii 是奇数位置,且 Pi>Pi+1P_i > P_{i+1},则交换 PiP_iPi+1P_{i+1}
    • 如果 ii 是偶数位置,且 Pi<Pi+1P_i < P_{i+1},则交换 PiP_iPi+1P_{i+1}
  • 这样,每次操作都确保当前位置满足“Zigzag”条件,且不会破坏之前已经满足条件的位置。
操作次数的限制
  • 每个位置最多只需要一次交换操作。
  • 由于总共有 2N2N 个元素,而每次操作涉及两个相邻元素,因此最多需要 NN 次操作。
正确性
  • 通过上述方法,我们可以确保每个奇数位置 ii 满足 Pi<Pi+1P_i < P_{i+1},每个偶数位置 ii 满足 Pi>Pi+1P_i > P_{i+1}
  • 由于每次操作只影响当前和下一个元素,不会影响之前已经满足条件的位置,因此最终排列一定满足“Zigzag”条件。
示例验证
  • 以样例输入1为例:
    • 初始排列:P=(4,3,2,1)P = (4, 3, 2, 1)
    • 检查位置1(奇数):4>34 > 3,交换后 P=(3,4,2,1)P = (3, 4, 2, 1)
    • 检查位置2(偶数):4>24 > 2,无需交换。
    • 检查位置3(奇数):2>12 > 1,交换后 P=(3,4,1,2)P = (3, 4, 1, 2)
    • 最终排列满足“Zigzag”条件。

总结

通过从左到右依次检查每个位置,并在必要时交换相邻元素,我们可以在最多 NN 次操作内将任意排列转换为满足“Zigzag”条件的排列。这一方法的时间复杂度为 O(N)O(N),能够在题目约束下高效运行。

思路

看了算法标签,得知这道题要用贪心来做,然后根据题意模拟即可。具体思路如下:
  1. ii11 循环到 2N12N−1,每次加上 22,如果 P[i]P[i+1]P[i] \geq P[i+1],那么就交换。
  2. 同样地,将 ii22 循环到 2N22N−2,每次加上 22,如果 P[i]P[i+1]P[i] \le P[i+1],那么也交换。

代码

CPP
#include<iostream>
using namespace std;

int p[200005], ans[200005];

int main() {
    int n, cnt = 0;
    cin >> n;
    n *= 2;

    // 输入数组
    for (int i = 1; i <= n; i++) {
        cin >> p[i];
    }

    // 处理偶数位置
    for (int i = 2; i <= n - 2; i += 2) {
        if (p[i] <= p[i + 1]) {
            swap(p[i], p[i + 1]);
            ans[++cnt] = i;
        }
    }

    // 处理奇数位置
    for (int i = 1; i <= n - 1; i += 2) {
        if (p[i] >= p[i + 1]) {
            swap(p[i], p[i + 1]);
            ans[++cnt] = i;
        }
    }

    // 输出结果
    cout << cnt << endl;
    for (int i = 1; i <= cnt; i++) {
        cout << ans[i] << " ";
    }

    return 0;
}

代码说明

  1. 输入部分
    • 读取 nn 并将其乘以 22,表示数组的长度。
    • 读取数组 p[] 的值。
  2. 处理偶数位置
    • 遍历偶数位置 i,如果 p[i] <= p[i+1],则交换这两个元素,并记录交换的位置。
  3. 处理奇数位置
    • 遍历奇数位置 i,如果 p[i] >= p[i+1],则交换这两个元素,并记录交换的位置。
  4. 输出结果
    • 输出交换的次数 cnt
    • 输出所有交换的位置。

示例

输入:

CPP
3
1 2 3 4 5 6

输出:

CPP
2
2 1

复杂度分析

  • 时间复杂度O(n)O(n),其中 n 是数组的长度。
  • 空间复杂度O(n)O(n),用于存储交换的位置。

评论

0 条评论,欢迎与作者交流。

正在加载评论...