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)的所有元素顛倒排序,即求出下一個序列了。