正经题解|上一个排列
2024-03-25 10:06:30
发布于:浙江
92阅读
0回复
0点赞
题目解析
给定的 数组本质上就是规定了一个排列的顺序,我们使用一个 std::map
离散化处理下后,按照常规的 的排列处理即可。
接下来我们看如何获取一个排列的上一个排列。
算法分析
的定义是:给定数字序列的字典序中上一个更小的排列。如果不存在上一个更小的排列,则将数字重新排列成最大的排列(即降序排列)。
以 的排列为例:
123
132
213
231
312
321
那么如果获取一个排列的上一个排列呢?
我们希望上一个排列比当前排列小,这样才满足 “上一个排列” 的定义。
因此只需要将排列后面的 与前面的 交换,就能得到一个更小的排列。比如 ,将 和 交换就能得到一个更小的排列 。
我们还希望上一个排列变小的幅度尽可能地小,这样才满足“上一个排列与当前排列紧邻”的要求。
为了满足这个要求,我们需要:
-
在 进行交换,需要 查找。
-
将一个 与 交换。比如 ,上一个排列应该把 和 交换而不是把 和 交换。
-
将 换到前面后,需要将 后面的所有数 ,降序排列就是最大的排列。
以 为例:首先按照上一步,交换 和 ,得到 ;然后需要将 之后的数重置为 ,得到 。显然 比 更大, 就是 的上一个排列。
算法流程
- 从后向前查找 的元素对 ,满足 。此时 必然是升序。
- 在 从后向前查找第一个满足 的 。、 分别就是上文所说的 、。
- 将 与 交换。
- 这时 必然是升序(参考步骤 ),反转 ,使其降序。
- 如果在步骤 找不到符合要求的相邻元素对,说明当前 为一个升序排列,则直接跳到步骤 。
AC代码
#include <bits/stdc++.h>
using namespace std;
void prev_permutation(vector<int> &p) {
int n = p.size();
int i = n - 1;
while (i > 0 and p[i - 1] <= p[i]) --i;
if (i > 0) {
int j = n - 1;
while (j >= i and p[j] >= p[i - 1]) --j;
swap(p[i - 1], p[j]);
}
reverse(begin(p) + i, end(p));
}
int main() {
cin.tie(nullptr)->sync_with_stdio(false);
int n; cin >> n;
vector<int> ori(n), cur(n);
map<int, int> mp;
for (int i = 0; i < n; ++i) {
cin >> ori[i];
mp[ori[i]] = i;
}
vector<int> id(n);
for (int i = 0; i < n; ++i) {
cin >> cur[i];
id[i] = mp[cur[i]];
}
prev_permutation(id);
for (int i = 0; i < n; ++i)
cout << ori[id[i]] << " \n"[i + 1 == n];
return 0;
}
复杂度分析
std::map
做离散化的时间复杂度为 ;
获取上一个排列的时间复杂度为 。
这里空空如也
有帮助,赞一个