碰到了一些问题 想明白之后来写题解
注释都在代码里啦
#include <iostream>
#include <cstdio>
#include <cstring>
typedef long long ll;
const int N = 21;
ll f[N][N][2];//f[i][j][k] i块木板,最左边的木板在这i快木板中排名为j,状态为k(k = 1表示高),能组成的方案数
int used[N];
void init()
{
f[1][1][1] = f[1][1][0] = 1;
for (int i = 2; i < N; i++)
for (int j = 1; j <= i; j++)
{
//若上一轮选的j,这一轮元素减少一个,排名为j的实际上是上一轮的j + 1,即此轮的j比上一轮j更大
for (int k = 1; k < j; k++)
f[i][j][1] += f[i - 1][k][0];
for (int k = j; k < i; k++)//要处于低位,不可能是最高的i
f[i][j][0] += f[i - 1][k][1];
}
}
int main()
{
init();
int t, n;
ll c;
scanf("%d", &t);
while (t--)
{
memset(used, 0, sizeof used);
scanf("%d%lld", &n, &c);
//第一块可能处于高位也可能处于低位
int last, k;
for (int j = 1; j <= n; j++)
{
if (f[n][j][1] >= c)
{
last = j, k = 1;
break;
}
else c -= f[n][j][1];
if (f[n][j][0] >= c)
{
last = j, k = 0;
break;
}
else c -= f[n][j][0];
}
used[last] = 1;
printf("%d", last);
for (int i = 2; i <= n; i++)
{
k ^= 1;
int rank = 0;//记j在剩下的木板中的排名
for (int j = 1; j <= n; j++)
{
if (used[j]) continue;
rank++;
if ((j < last && k == 0) || (j > last && k == 1))
{
if (f[n - i + 1][rank][k] >= c)
{
last = j;
used[last] = 1;
printf(" %d", last);
break;
}
else c -= f[n - i + 1][rank][k];
}
}
}
printf("\n");
}
return 0;
}