题目链接:
题目大意:
定义一种排列的标准循环表示法,也就是排列中先按每个循环内部的从大到小排序,然后以每个循环的最大值从小到大排序,问存在的第k小的符合要求的排列
题目分析:
首先有一点是一定要发现的,如果两个数在同一循环中
设他们的下标为i,j,i != j , 若i < j
那么换完之后ai = j , aj = i, ai > aj
如果i,j不相邻,那么存在k满足i<k<j,如果k和大于j的换,那么ak>aj,不合法
如果k和小于i的交换,那么ak<ai不合法
所以i=j-1
因为交换具有传递性,所以如果ai与aj交换,aj和ak交换,相当于ai和ak交换,而ai和ak不相邻,所以不合法
那么也就是得到一个结论:只有相邻的值可以交换,且每个数最多做一次交换
那么假设n的排列的合法的数量为dp[n]
那么dp[n] = dp[n-1] + dp[n-2]
也就是第n个不和第n-1个交换,以及第n个和第n-1个交换的情况
那么第k个合法排列也就是,因为只可以得到相邻交换得到序位更大的合法序列,所以从最后一位开始,逐渐判断是否要交换即可,逐位确定最后要构造出序列的每一位即可
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#define MAX 57
using namespace std;
typedef long long LL;
int n;
LL dp[MAX],k;
int num[MAX];
void init ( )
{
dp[0] = dp[1] = 1;
for ( int i = 2 ; i < MAX ; i++ )
dp[i] = dp[i-1] + dp[i-2];
}
int main ( )
{
init();
while ( ~scanf ( "%d%I64d" , &n , &k ) )
{
for ( int i = 1 ; i <= n ; i++ )
{
if ( k > dp[n-i] )
{
k -= dp[n-i];
num[i] = i+1;
num[i+1] = i;
i++;
}
else num[i] = i;
}
for ( int i = 1 ; i <= n ; i++ )
printf ( "%d " , num[i] );
}
}