Description
We all know that , when there are n positive integers (namely 1…n) , we can use these n integers to construct n! kinds of permutations . Sort these permutations in lexicographic order . For example , when n = 3 , we can permute 1 2 3,1 3 2,2 1 3, 2 3 1, 3 1 2 , 3 2 1 these 6 kinds of permutations.
Now , we want to know , with the given permutation , calculate the next k permutation . If the permutation is the last permutation in order , its next permutation is the first permutation , namely 1 2 3 … n.
e.g. when n = 3 , k = 2 , giving the permutation 2 3 1 , thus its next 1 permutation is 3 1 2 , its next 2 permutation is 3 2 1 , so the answer is 3 2 1 .
Input
The first line is a positive integer m , indicating the number of test cases . Then m test cases . In every test case , the first line is 2 positive integers n (1 <= n < 1024) and k (1 <= k <= 64) ; the second line are n positive integers , which is one of the “ 1 2 3 … n” s’ permutation.
Output
For each test case , print one line , n numbers , with spaces separating every number , indicating the given permutation ‘s next k permutation.
Sample Input
3
3 1
2 3 1
3 1
3 2 1
10 2
1 2 3 4 5 6 7 8 9 10
Sample Output
3 1 2
1 2 3
1 2 3 4 5 6 7 9 8 10
自己写的程序(非常麻烦,其实算法的原理很简单,我真是个笨鳖.....):
package OJ;
import java.util.*;
public class P5_temp { //Permutation
public static void main(String[] args) {
class group {
int[] group;
public group(int[] group) {
this.group = group;
}
public int[] get() {
return group;
}
}
Scanner in = new Scanner(System.in);
int caseNum = in.nextInt();
in.nextLine();
//这里需要一些东西
ArrayList<group> result = new ArrayList<group>();
for(int i = 0; i < caseNum; i++) {
int orderNum = in.nextInt();
//收集原来的排列
int[] order = new int[orderNum];
//往下变化的数量
int nextNum = in.nextInt();
in.nextLine();//
for(int j = 0; j < orderNum; j++) {
order[j] = in.nextInt();
}
//收集数据结束
//开始进行往下进行变化
order = findPermutation(order, nextNum, order.length-1);
result.add(new group(order));
if(i < caseNum-1)
in.nextLine();
}
for(group g : result) {
int[] t = g.get();
for(int i = 0; i < t.length; i++){
if(i == t.length -1){
System.out.println(t[i]);
System.out.println();
}
else {
System.out.print(t[i] + " ");
}
}
}
}
public static int[] findPermutation(int[] num, int nextNum, int end) {//nextNum是需要变化的次数
boolean is = false;
int loc = end -1;
TreeSet<Integer> od = new TreeSet<Integer>();
//一边比较,一边排序
for(; end >= 0; end--,loc--) { //第一个循环
boolean skip = false; //是否跳出第一个循环的标志
//假如前一个比后面有序序列的最大值小,则交换,每交换一次,则改变次数+1
if(loc < 0)
od.add(num[end]);
else {
od.add(num[end]);
if(num[loc] < od.last()) {
int bigger = od.subSet(od.higher(num[loc]), od.last()+1).size();
if(bigger*calculateGroupNum(od.size()) < nextNum) { //假如后续的所有变化都不能满足变化数
nextNum = nextNum - bigger*calculateGroupNum(od.size());
continue;
}
else { //假如后续的变化可以满足nextNum的要求
Iterator<Integer> iter = od.iterator();
while(iter.hasNext()) { //第二个循环,不断的从后续队列中抽取比num[loc]大的元素
int y = iter.next();
if(y > num[loc]){
nextNum--;
if(nextNum == 0) { //假如nextNum为0,退出循环,!!!!返回数组(需要将od中的覆盖到原数组)!!!!!!
od.add(num[loc]);
num[loc] = y;
od.remove(y);
skip = true;
break;
}
else {
int cg = calculateGroupNum(od.size()) - 1;
if(cg < nextNum) { //假如之后的序列不能将nextNum变为<=0,则不用进行具体的变化
nextNum = nextNum - cg;
continue;
}
else if(cg == nextNum) { //返回数组(将od中的数以倒叙覆盖到原数组)应该!!!!!!
nextNum = 0;
is = true;
skip = true;
od.add(num[loc]);
num[loc] = y;
od.remove(y);
break;
}
else { //这时可以进行具体的变化(因为nextNum<排列组合数)
od.add(num[loc]);
num[loc] = y;
od.remove(y);
for(int i = num.length-od.size(); i < num.length ; i++)
num[i] = od.pollFirst();
num = findPermutation(num, nextNum, num.length-1);
break;
}
}
}
}
}
}
}
// //假如前一个比后面的有序序列的最大值大,则将num[loc]加入到有序序列
// else {
// od.add(num[loc]);
// }
if(skip == true)
break;
}
if(nextNum == 0 && end > 0 && is == false){ //这就是发现了最终的答案
for(int i = num.length-od.size(); i < num.length ; i++)
num[i] = od.pollFirst();
return num;
}
else if (nextNum == 0 && end > 0 && is == true) {
for(int i = num.length-od.size(); i < num.length ; i++)
num[i] = od.pollLast();
return num;
}
else if (end == -1 && nextNum >0) { //这是需要循环到整体最小的序列,重新开始
for(int i = 0; i < num.length ; i++)
num[i] = od.pollFirst();
nextNum--;
if(nextNum == 0)
return num;
else
return findPermutation(num, nextNum, num.length - 1);
}
else
return null;
}
public static int calculateGroupNum(int Num) { //计算排列组合的最大值
int sum = 1;
for(int i = 1; i <= Num; i++)
sum *= i;
return sum;
}
}
题目要求的算法:实际上使用c++STL的next_permutation方法,可以很快的得出结果。下面是关于这个函数的介绍:
next_permutation函数实现原理如下:
在当前序列中,从尾端往前寻找两个相邻元素,前一个记为*i,后一个记为*ii,并且满足*i < *ii。然后再从尾端寻找另一个元素*j,如果满足*i < *j,即将第i个元素与第j个元素对调,并将第ii个元素之后(包括ii)的所有元素颠倒排序,即求出下一个序列了。