题意简述:由1——n组成的排列,若其元素大小交替,则称为可爱排列,给定n和k,求所有可爱排列中字典序第k小的可爱排列。
分析:利用康托展开的思想,逐位计算排列数来确定该位数字,但不同的是,这里的排列带限制,因此需要先dp处理出f[i][j][k]表示1到i组成的排列数中以j开头的可爱排列数,k用0、1表示j相对于第二个元素的大小,转移方程为:,时间复杂度为O(t*n^2)。
代码:
#include<stdio.h>
#define int long long
const int N=20+3;
int f[N][N][2];
bool vis[N];
void dp(int n=20)
{
f[1][1][1]=f[1][1][0]=1;
for (int i=2;i<=n;i++)
{
f[i][i][0]=0;
for (int j=i-1;j>=1;j--)
f[i][j][0]=f[i][j+1][0]+f[i-1][j][1];
f[i][1][1]=0;
for (int j=2;j<=i;j++)
f[i][j][1]=f[i][j-1][1]+f[i-1][j-1][0];
}
}
signed main()
{
dp();
int t; scanf("%lld",&t);
while (t--)
{
int n,num; scanf("%lld%lld",&n,&num);
num--;
for (int i=1;i<=n;i++) vis[i]=false;
vis[0]=true;
int last;
for (int i=1;i<=n;i++)
if (num>=f[n][i][0]+f[n][i][1])
num-=f[n][i][0]+f[n][i][1];
else
{
vis[i]=true; printf("%lld",i);
last=i; break;
}
bool p;
for (int i=2;i<n;i++)
{
bool flag=false; int count=0;
for (int j=1;j<=n;j++)
{
if (vis[j-1]) continue;
count++;
if (i^2)
{
if (p)
if (last<j-1) continue;
if (!p)
if (last>j-1) continue;
}
int add=j-1>last?f[n-i+1][count][1]:f[n-i+1][count][0];
if (num>=add) num-=add;
else
{
vis[j-1]=true; printf(" %lld",j-1);
p=(last<j-1); last=j-1; flag=true;
break;
}
}
if (!flag)
{
vis[n]=true; printf(" %lld",n);
p=(last<n); last=n;
}
}
for (int i=1;i<=n;i++)
if (!vis[i]) {printf(" %lld",i);break;}
printf("\n");
}
return 0;
}