1 条题解
-
0
问题背景:
给定一个长度为 的数组,要求通过若干次“交换操作”将其排序。这里的交换操作并不是普通的两两交换,而是一次性选择一组下标,按照某种规则进行配对交换。程序的目标是输出最多 轮这样的操作,使得数组有序。核心思路:
- 排序与置换映射:
- 输入数组 ,记录其原始下标。
- 按值排序后,得到每个元素在有序数组中的目标位置。
- 构建置换映射 :表示原始位置 的元素应该去的位置。
- 循环分解:
- 置换可以分解为若干个循环节。
- 若一个循环节长度大于 ,则需要通过交换操作打破循环。
- 遍历所有循环节,记录每个循环节中的元素。
- 首尾配对交换:
- 对于循环节 ,从两端向中间配对:
- 将 与 交换,更新 。
- 记录这对下标,分别放入 和 。
- 这样一轮操作可以同时处理多个循环节。
- 构造交换序列:
- 将 反转后与 拼接,得到本轮的交换序列。
- 若本轮没有需要交换的元素,说明排序完成。
- 否则将该序列加入答案 。
- 轮数限制:
- 理论上最多只需要 轮操作即可完成排序。
- 程序中有断言
assert(ans.size() <= 2)
保证正确性。
- 输出:
- 输出轮数 。
- 对于每一轮,输出交换的元素个数和具体下标。
复杂度分析:
- 排序 。
- 循环分解与交换 。
- 总体复杂度 ,适合 。
#include <bits/stdc++.h> using namespace std; typedef vector <int> vi; const int N = 3e3 + 5; // 定义最大数组大小 int n, k[N]; // n: 元素个数, k: 存储置换的映射关系 bool vis[N]; // 标记数组,用于记录元素是否被访问过 pair <int, int> a[N]; // 存储(值, 原始索引)的数组 vector <vi> ans; // 存储所有交换操作的答案 signed main() { ios :: sync_with_stdio(false); cin.tie(nullptr); cin >> n; // 读入数据并记录原始位置 for (int i = 1; i <= n; i++) cin >> a[i].first, a[i].second = i; // 按值排序,得到排序后的位置关系 sort(a + 1, a + n + 1); // 构建置换映射:k[i] 表示原始位置i的元素在排序后应该去的位置 for (int i = 1; i <= n; i++) k[a[i].second] = i; // 通过循环交换操作将数组排序 while (1) { vi A, B; // A存储前半部分要交换的元素,B存储后半部分 fill(vis + 1, vis + n + 1, 0); // 重置访问标记 // 遍历所有元素,寻找循环节 for (int i = 1; i <= n; i++) { if (vis[i]) // 如果已经访问过,跳过 continue; vi cyc; // 存储当前循环节中的元素 // 遍历整个循环节 for (int j = i; !vis[j]; j = k[j]) { cyc.push_back(j); // 将当前元素加入循环节 vis[j] = 1; // 标记为已访问 } // 在循环节内部进行交换:首尾配对交换 for (int l = 0, r = (int)cyc.size() - 1; l < r; l++, r--) { swap(k[cyc[l]], k[cyc[r]]); // 交换映射关系 A.push_back(cyc[l]); // 记录交换的第一个元素 B.push_back(cyc[r]); // 记录交换的第二个元素 } } // 准备本次循环的交换序列 reverse(A.begin(), A.end()); // 反转A使其按正确顺序排列 A.insert(A.end(), B.begin(), B.end()); // 将B添加到A的末尾 // 如果没有需要交换的元素,排序完成 if (A.empty()) break; // 将本次的交换序列加入答案 ans.push_back(A); } // 断言:最多只需要2轮交换操作 assert(ans.size() <= 2); cout << ans.size() << "\n"; // 输出每轮交换的详细信息 for (auto z : ans) { cout << z.size() << "\n"; // 输出本轮交换操作的数量 // 输出本轮所有要交换的元素索引 for (auto x : z) cout << x << " "; cout << "\n"; } return 0; }
- 1
信息
- ID
- 3183
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 2
- 已通过
- 1
- 上传者