动态规划
仅考虑相对长度;
状态转移方程:
占个坑先。。。
输出全排列
占个坑先。。。
代码如下:
#include<iostream>
using namespace std;
int k, n;
long long c;
long long rise[21][21] = { 0 }, fall[21][21] = { 0 };
int result[20] = { 0 };
// rise[i][j]表示长度为i,开头为第j长的数所组成的排列的集合(因为考虑相对长度就行了吧)
// 通过删去第一个数,且对后续数列中大于第一个数的都-1(代表剩下数字中的第k个数),就构造了i与i-1的一一映射。
void dp() {
rise[1][1] = 1; fall[1][1] = 1;
for (int i = 2; i <= 20; ++i) {
long long sum_fall = 0, sum_rise = 0;
for (int k = 1; k <= i - 1; ++k) sum_fall += fall[i - 1][k];
for (int j = 1; j <= i; ++j) {
rise[i][j] = sum_fall;
fall[i][j] = sum_rise;
if (j < i) {
sum_fall -= fall[i - 1][j];
sum_rise += rise[i - 1][j];
}
}
}
}
void getans() {
bool isrise = false;
bool isseen[20] = { false };
for (int i = 1; i <= n; ++i) {
if (rise[n][i] + fall[n][i] >= c) {
result[0] = i;
if (c > fall[n][i]) {
isrise = true;
c -= fall[n][i];
}
break;
}
else c -= (rise[n][i] + fall[n][i]);
}
for (int i = 1; i < n; ++i) {
isrise = !isrise;
if (isrise) {
for (int j = 1; j < result[i - 1]; ++j) {
if (rise[n - i][j] < c) c -= rise[n - i][j];
else {
result[i] = j;
break;
}
}
}
else {
for (int j = result[i - 1]; j <= n - i; ++j) {
if (fall[n - i][j] < c) c -= fall[n - i][j];
else {
result[i] = j;
break;
}
}
}
}
for (int i = 0; i < n; ++i) {
int cnt = 0;
for (int j = 0; j < n; ++j) {
if (!isseen[j]) cnt++;
if (cnt == result[i]) {
isseen[j] = true;
cout << j + 1 << " ";
break;
}
}
}
cout << endl;
}
int main(){
dp();
cin >> k;
for (int i = 0; i < k; ++i) {
cin >> n >> c;
getans();
}
return 0;
}