poj 1833 排列
Time Limit: 1000MS Memory Limit: 30000K
Total Submissions: 24036 Accepted: 8727
Description
题目描述:
大家知道,给出正整数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
问题链接: 题目来源
解题思路:
这是一道经典的模拟问题,使用模拟法解题时注意对问题进行分析然后抽离主要步骤,将现实的实物映射成计算机能够识别的符号代码,而将现实实物之间的关系分别映射成运算或逻辑控制流。
模拟法通常不需要高超的算法技巧,只需要读者可以理解问题,抓住问题的关键,对于没有一些具体数值解法的题目来讲,模拟法是最好的办法。
这道题的大意为给定一个序列 cnt[n] 求出这个序列后第 k 个序列,例如序列 1、2、3 的后的第2个序列为 2、1、3。
我的想法如下图:
而该算法的关键在于找到给定排列的下一个排列:
-
使用C++ 库中的next_permutation( ) 函数。
具体用法为:
int a[]={1,2,3,4,5}; //产生所有下一组合,时间复杂度为n!,速度较慢
next_permutation(a,a+5); //求出a序列的后第5个序列。 -
当然如果你不知道这个函数的话也可以自己造轮子,具体思路如下:
具体ac代码如下:
#include<bits/stdc++.h> //万能头文件有些编译器可能不能通过
using namespace std;
int m, n, k; //m为测试个数 n为 n个正整数组成排列 k为后面第几个排列
#define MAXN 1030
int cnt[MAXN] = {0};
void NextPermutation(int size) //找出此排列后的最小排列
{
int flag = size - 1; //先默认最右边为标志 假设是最大的数字
while(cnt[flag-1] > cnt[flag] && flag != 0)
flag--; //得出flag为从右往左数 左边的的数字小于此数的数字(或者为 0的数字) 设该数字为min数
if(flag == 0)
{
for(int i = 0; i < size; i++)
cnt[i] = i + 1;
return;
}
for(int i = size - 1; i >= flag; i--)
{
if(cnt[i] > cnt[flag -1])
{
swap(cnt[i], cnt[flag - 1]); //找到第一个从左往右数第一个大于flag 左边的数字
break;
}
}
//重新排列flag右边的数字 从小到大排列
while(size -1 > flag)
{
swap(cnt[flag], cnt[size - 1]);
flag ++;
size --;
}
}
int main()
{
scanf("%d",&m); //表示测试的个数共有m 个
while(m--)
{
scanf("%d%d", &n, &k);
for(int i = 0; i < n; i++)
scanf("%d", &cnt[i]);
for(int i = 0; i < k; i++)
NextPermutation(n); //重复求cnt的下一个排列 更新cnt
for(int i = 0; i < n; i++)
printf("%d ", cnt[i]);
printf("\n");
}
return 0;
}