1 条题解
-
0
根据标程思路,下面给出本题的详细题解。
题意回顾
给定一个长度为 的排列 。
对于每个位置 ,可以选择 或 。目标是使数组 的逆序对数最少,求该最小值。
核心观察
考虑排列中的最小值 ,设其所在位置为 。
- 若选择 ,则 是整个数组的最小值,它与左边所有元素(共 个)都构成逆序对,与右边元素不构成任何逆序对,贡献为 。
- 若选择 ,则 变为最大值,它与右边所有元素(共 个)构成逆序对,与左边无逆序对,贡献为 。
这两个选择带来的逆序对数量仅与 的位置有关,与其他元素的具体选择完全无关。因此,为了最小化总逆序对数,我们可以直接贪心地选择贡献较小的方案,即取 。
之后,将元素 从原排列中移除,剩余 个元素相对大小关系不变,相当于原排列中的 变成了新的最小值 ,问题规模缩小为 ,且与原问题完全同构。递归地应用上述策略即可得到全局最优解。
算法步骤
上述过程等价于:对排列中的每个元素 ,分别计算它左侧比 大的元素个数 和右侧比 大的元素个数 ,然后将 累加到答案中。
为什么这样可以?
当按照元素值从小到大的顺序依次处理时,对于当前最小值 ,它左侧比 大的元素个数就是 ,右侧比 大的元素个数就是 。移除 后,剩下的元素中,原来的 成为新的最小值,它的 和 在当前剩余集合中仍保持原来的数值(因为移除的 比它小,不影响比它大的计数)。因此,对所有元素独立计算并取 再求和即得到答案。
实现细节
由于 ,可以在 时间内直接计算。
对于每个位置 :- 的数量。
- 的数量。
然后累加 。
也可以用树状数组优化到 ,但本题数据范围不需要。
正确性证明(归纳)
- 当 时,逆序对数为 ,公式正确。
- 假设对长度为 的排列,按上述方法得到的值是最小逆序对数。
- 对于长度为 的排列,取最小值 所在位置 ,无论其他元素如何选择, 变为 或 造成的逆序对贡献是独立的,贪心选择 不会使总答案更劣。移除 后,剩余部分的最优解由归纳假设给出。
因此算法正确。
复杂度
- 时间复杂度:每个测试用例 ,总复杂度 ,在时限内。
- 空间复杂度:。
代码
#include <bits/stdc++.h> using namespace std; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int t; cin >> t; while (t--) { int n; cin >> n; vector<int> p(n); for (int i = 0; i < n; ++i) { cin >> p[i]; } int ans = 0; for (int i = 0; i < n; ++i) { int lbigger = 0, rbigger = 0; for (int j = 0; j < i; ++j) { if (p[j] > p[i]) lbigger++; } for (int j = i + 1; j < n; ++j) { if (p[j] > p[i]) rbigger++; } ans += min(lbigger, rbigger); } cout << ans << " \n"; } return 0; }
- 1
信息
- ID
- 7119
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 4
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者