02996: 选课
two pointers, http://cs101.openjudge.cn/practice/02996
教务网站如期的在选课之日出问题了,这次的问题是登陆窗口的验证码无法显示了,同学们只能靠猜验证码来登陆选课。教务的登陆系统刚刚经过改进,改进后的验证码均为1..N的一个排列。一般的同学们在试验的时候都是按照所有排列的字典序逐个试验,但是TN发掘这样试验很乏味,所以他决定每次尝试前一个排列后面的第M个排列。
但是一段时间之后他发现,寻找一个排列后面的第M个排列并不是一件容易的事情,所以他希望你帮助他。
输入
Line 1: N (1 <= N <= 10000) Line 2: M (1 <= M <= 100) Line 3: 1..N的一个排列
输出
所求的排列
样例输入
5
3
1 2 3 4 5样例输出
1 2 4 5 3来源: 第六届北京大学程序设计大赛暨ACM/ICPC选拔赛
python
# https://leetcode.cn/problems/next-permutation/
'''
方法一:两遍扫描
思路及解法
注意到下一个排列总是比当前排列要大,除非该排列已经是最大的排列。我们希望找到一种方法,
能够找到一个大于当前序列的新序列,且变大的幅度尽可能小。具体地:
我们需要将一个左边的「较小数」与一个右边的「较大数」交换,以能够让当前排列变大,从而得到下一个排列。
同时我们要让这个「较小数」尽量靠右,而「较大数」尽可能小。当交换完成后,「较大数」
右边的数需要按照升序重新排列。这样可以在保证新排列大于原来排列的情况下,使变大的幅度尽可能小。
以排列 [4,5,2,6,3,1] 为例:
我们能找到的符合条件的一对「较小数」与「较大数」的组合为 2 与 3,满足「较小数」尽量靠右,而「较大数」尽可能小。
当我们完成交换后排列变为 [4,5,3,6,2,1],此时我们可以重排「较小数」右边的序列,序列变为 [[4,5,3,1,2,6]。
具体地,我们这样描述该算法,对于长度为 n 的排列 a:
首先从后向前查找第一个顺序对 (i,i+1),满足 a[i]<a[i+1]。这样「较小数」即为 a[i]。此时 [i+1,n) 必然是下降序列。
如果找到了顺序对,那么在区间 [i+1,n) 中从后向前查找第一个元素 j 满足 a[i]<a[j]。这样「较大数」即为 a[j]。
交换 a[i] 与 a[j],此时可以证明区间 [i+1,n) 必为降序。我们可以直接使用双指针反转区间 [i+1,n) 使其变为升序,
而无需对该区间进行排序。
'''
from typing import List
def nextPermutation(nums: List[int]) -> None:
i = len(nums) - 2
while i >= 0 and nums[i] >= nums[i + 1]:
i -= 1
if i >= 0:
j = len(nums) - 1
while j >= 0 and nums[i] >= nums[j]:
j -= 1
nums[i], nums[j] = nums[j], nums[i]
left, right = i + 1, len(nums) - 1
while left < right:
nums[left], nums[right] = nums[right], nums[left]
left += 1
right -= 1
n = int(input())
m = int(input())
arr = list(map(int, input().split()))
for k in range(m):
nextPermutation(arr)
print(*arr)