POJ 1833 排列(字典序全排列、next_permutation()、copy+流迭代器)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/AgoniAngel/article/details/51882657

题意:给全排列中的一个排列,输出接下来第k个排列(按字典序排列)

具体:

题目描述: 
大家知道,给出正整数n,则1到n这n个数可以构成n!种排列,把这些排列按照从小到大的顺序(字典顺序)列出,如n=3时,列出1 2 3,1 3 2,2 1 3,2 3 1,3 1 2,3 2 1六个排列。 


任务描述: 
给出某个排列,求出这个排列的下k个排列,如果遇到最后一个排列,则下1排列为第1个排列,即排列1 2 3…n。 
比如:n = 3,k=2 给出排列2 3 1,则它的下1个排列为3 1 2,下2个排列为3 2 1,因此答案为3 2 1。 


Input
第一行是一个正整数m,表示测试数据的个数,下面是m组测试数据,每组测试数据第一行是2个正整数n( 1 <= n < 1024 )和k(1<=k<=64),第二行有n个正整数,是1,2 … n的一个排列。


Output
对于每组输入数据,输出一行,n个数,中间用空格隔开,表示输入排列的下k个排列。


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


思路:需要注意最后一个排列(如5 4 3 2 1)的下一个排列是第一个排列(1 2 3 4 5)

解法一(超时):

对于按字典序生成的排列,由一个排列生成下一个排列的算法如下

【例】 如何得到346987521的下一个
    1,从尾部往前找第一个P(i-1) < P(i)的位置
            3 4 6 9 8 7 5 2 1
        最终找到6是第一个变小的数字,记录下6的位置i-1
    2,从i位置(例中的9)开始往后找到最后一个大于6的数(可以从后往前找)

            3 4 6 (9 8 7 5 2 1)
        最终找到7的位置,记录位置为j
    3,交换位置i-1和j的值
            3 4 7 (9 8 6 5 2 1)
    4,将i~n-1的元素按升序排序
            3 4 7 (1 2 5 6 8 9)
    则347125689为346987521的下一个排列


采用这种算法会超时(包括网上的“题解”,可能是这题数据加强了)。

超时代码如下:

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;

void getNext(int *a,int n){
	int ii= -1, jj;
	for (int i = n-1; i > 0; i--){
		if (a[i] > a[i - 1]) {
            ii = i-1;
            break;
		}
	}
	if (ii == -1) {
		for (int i = 0; i < n; i++) a[i] = i + 1;
		return ;
	}
	for (int j = n-1;j>= ii + 1; j--)
		if (a[j]>a[ii]) {jj = j;break;}
	swap(a[ii], a[jj]);
	sort(a+ii + 1, a+n);
	return ;
}

int main(){
	int a[1025], k, n, T;
	cin >> T;
	while (T--){
		scanf("%d%d", &n,&k);
		for (int i = 0; i < n; i++){
			scanf("%d", &a[i]);
		}
		for (int i = 0; i < k; i++){
			getNext(a, n);
		}
		printf("%d", a[0]);
		for (int i = 1; i < n; i++) printf(" %d", a[i]);
		printf("\n");
	}


	return 0;
}

解法二:用STL的next_permutation()函数,如果只有这个函数还是会超时,解决方法是在输出时用copy函数+流迭代器。参考 点击打开链接

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <iterator>
using namespace std;

int main(){
	int a[1025], k, n, T;
	cin >> T;
	while (T--){
		scanf("%d%d", &n,&k);
		for (int i = 0; i < n; i++){
			scanf("%d", &a[i]);
		}
		for (int i = 0; i < k; i++){
            if(!next_permutation(a,a+n))                   //其实不用特判最后一个排列,如果是最后一个排列,next_permutation的结果是回到第一个排列,同时这样也会更省时
                for(int i=0;i<n;i++)
                    a[i]=i+1;
		}
		copy(a,a+n-1,ostream_iterator<int>(cout," "));
		printf("%d\n",a[n-1]);
	}
}


猜你喜欢

转载自blog.csdn.net/AgoniAngel/article/details/51882657