手写原地 next permutation,连续执行 M 次,得到当前排列之后第 M 个字典序排列。
OJ: luogu
题目 ID: P1088
难度:普及-
标签:排列模拟python
日期: 2026-07-15 21:40
题意
给定 1..N 的一个排列,以及一个很小的整数 M。要求输出这个排列在字典序中向后移动 M 次后的排列。
N 可以到 10000,不能生成所有排列。
思路
需要手写“下一个排列”。
从右往左看,找到第一个还能变大的位置 i,也就是:
text
a[i] < a[i + 1]此时 i 右边的后缀是降序的。为了得到刚好大一点的排列:
- 在后缀中从右往左找第一个大于
a[i]的数; - 交换它和
a[i]; - 把后缀反转成升序。
因为 M <= 100,执行 M 次 next_permutation 即可。
Python 知识
- 列表可以原地交换:
a[i], a[j] = a[j], a[i]。 - 切片赋值
a[i + 1:] = reversed(a[i + 1:])可以把后缀反转后写回原列表。 print(*numbers)用空格输出整个排列。
参考笔记:
/home/rainboy/mycode/hugo-blog/content/program_language/python/sorting_and_ordering.md/home/rainboy/mycode/hugo-blog/content/program_language/python/input_output_and_strings.md
代码
python
def next_permutation(a):
n = len(a)
i = n - 2
while i >= 0 and a[i] > a[i + 1]:
i -= 1
j = n - 1
while a[j] < a[i]:
j -= 1
a[i], a[j] = a[j], a[i]
a[i + 1:] = reversed(a[i + 1:])
n = int(input())
m = int(input())
numbers = list(map(int, input().split()))
for _ in range(m):
next_permutation(numbers)
print(*numbers)cpp
/**
* Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
* rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
* rainboy的学习导航网站: https://idx.roj.ac.cn
* create_at: 2026-07-27 00:00
* update_at: 2026-07-27 00:00
*/
#include <bits/stdc++.h>
using namespace std;
int n, m;
int a[10005];
void next_perm() {
int i = n - 2;
while (i >= 0 && a[i] > a[i + 1]) i--;
int j = n - 1;
while (a[j] < a[i]) j--;
swap(a[i], a[j]);
reverse(a + i + 1, a + n);
}
int main() {
cin >> n >> m;
for (int i = 0; i < n; i++) cin >> a[i];
for (int t = 0; t < m; t++) next_perm();
for (int i = 0; i < n; i++) cout << a[i] << " ";
cout << endl;
return 0;
}复杂度
一次 next_permutation 是 M 次,总时间复杂度为
总结
这题不能把所有排列列出来,而是要掌握字典序下“下一个排列”的局部修改规则。