用長度為 1..N 的 N 根木板構建柵欄,要求:
a[i-1] < a[i] > a[i+1] 或 a[i-1] > a[i] < a[i+1])所有合法排列按字典序編號(從 1 開始)。給定 N (≤20) 和編號 C,輸出第 C 個排列。
設 dp[n][k][dir] 表示:用 1..n 中第 k 小的數作為首個元素,且首個趨勢為 dir 的波浪排列數。
dir = 0 (UP):首元素 < 第二個元素 (即 a[1] < a[2])
dir = 1 (DOWN):首元素 > 第二個元素
考慮首元素為第 k 小 (即值為 k),剩餘 n-1 個數中:
dp[n][k][0] = Σ dp[n-1][j][1] (j = k .. n-1) // 注意:剩餘數重新排序後的排名 dp[n][k][1] = Σ dp[n-1][j][0] (j = 1 .. k-1)
dp[1][1][0] = dp[1][1][1] = 1;
從左到右貪心決定每位:
used[] 標記已用數字x (按值從小到大):
x 未用,計算以 x 為第 i 位的合法排列數 cnt累積 + cnt < C,累積加上 cnt,繼續試下一個 xx 即為第 i 位答案,固定之,進入第 i+1 位計算 cnt 時需根據已確定的前綴趨勢判斷:
cnt = dp[n][rank][0] + dp[n][rank][1] (rank 為 x 在剩餘數中的排名)cnt = dp[rem][rank][1]cnt = dp[rem][rank][0]#include <bits/stdc++.h>
using namespace std;
using int64 = long long;
const int MAXN = 25;
int64 dp[MAXN][MAXN][2]; // dp[len][rank][dir] 0=UP, 1=DOWN
void initDP() {
memset(dp, 0, sizeof(dp));
dp[1][1][0] = dp[1][1][1] = 1;
for (int n = 2; n <= 20; ++n) {
for (int k = 1; k <= n; ++k) { // 首元素為第 k 小
// UP: 第二個 > 首元素 → 剩餘中選排名 k..n-1 的數,新趨勢 DOWN
for (int j = k; j <= n-1; ++j) dp[n][k][0] += dp[n-1][j][1];
// DOWN: 第二個 < 首元素 → 剩餘中選排名 1..k-1,新趨勢 UP
for (int j = 1; j <= k-1; ++j) dp[n][k][1] += dp[n-1][j][0];
}
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
initDP();
int T;
cin >> T;
while (T--) {
int n; int64 c;
cin >> n >> c;
vector ans(n+1);
vector used(n+1, false);
for (int i = 1; i <= n; ++i) {
int64 accum = 0;
// 枚舉候選值 v 從 1 到 n
for (int v = 1; v <= n; ++v) {
if (used[v]) continue;
// 計算 v 在剩餘數中的排名 (1-based)
int rank = 0;
for (int u = 1; u <= v; ++u) if (!used[u]) ++rank;
int rem = n - i + 1;
int64 cnt = 0;
if (i == 1) {
cnt = dp[rem][rank][0] + dp[rem][rank][1];
} else {
bool lastUp = (ans[i-2] < ans[i-1]);
if (lastUp) cnt = dp[rem][rank][1]; // 需 DOWN
else cnt = dp[rem][rank][0]; // 需 UP
}
if (accum + cnt >= c) {
ans[i] = v;
used[v] = true;
break;
}
accum += cnt;
}
}
for (int i = 1; i <= n; ++i) {
if (i > 1) cout << ' ';
cout << ans[i];
}
cout << '\n';
}
return 0;
}
long longint64E_n = Σk dp[n][k][0] (或 [1]),計數所有波浪排列E(n,k) = dp[n][k][0]本題將組合計數、DP、貪心構造完美融合,是排列枚舉進階的經典範例。