问题分析
题目要求找出字典序最小的 1 到 N 的排列,使得通过相邻数字相加直到只剩一个数的过程,最终结果等于给定的 sum。若无解,则不输出任何内容。
关键观察:最终的总和等于初始序列中每个数字乘以一个特定系数的和。这些系数是二项式系数,具体为组合数 C(n-1, i),其中 i 为数字在序列中的位置(0-indexed)。例如,当 n=4 时,系数为 [1, 3, 3, 1]。
解法思路
- 预处理组合数:计算 0 到 11 行的二项式系数(因为 N ≤ 12)。
- 构建系数数组:根据输入的 n,生成长度为 n 的系数数组,其中第 i 个元素为 C(n-1, i)。
- 预处理后缀系数:对于每个起始位置 pos,预先计算从 pos 到 n-1 的系数并降序排序,用于后续剪枝。
- 深度优先搜索 (DFS):
- 状态:当前放置的位置 pos,当前累加和 current_sum。
- 剪枝:在每一步,计算剩余位置的最小和最大可能贡献:
- 最小贡献:将剩余数字升序排列,与降序排列的剩余系数相乘求和。
- 最大贡献:将剩余数字降序排列,与降序排列的剩余系数相乘求和。
- 若 current_sum + 最小贡献 > sum 或 current_sum + 最大贡献 < sum,则剪枝。
- 字典序:按数字从小到大的顺序尝试放置,确保找到的第一个解为字典序最小。
- 终止条件:当放置完所有数字(pos == n)且 current_sum 等于 sum 时,输出序列并退出程序。
- 无解处理:若 DFS 结束未找到解,程序自然结束,无输出。
代码实现
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstdlib>
using namespace std;
int n, total_sum;
vector<int> coeff;
vector<vector<int>> suffix_coeffs;
vector<int> ans;
vector<bool> used;
bool dfs(int pos, long long current_sum) {
if (pos == n) {
if (current_sum == total_sum) {
for (int i = 0; i < n; i++) {
cout << ans[i];
if (i < n-1) cout << " ";
}
cout << endl;
exit(0);
}
return false;
}
vector<int> rem_nums;
for (int i = 1; i <= n; i++) {
if (!used[i]) {
rem_nums.push_back(i);
}
}
vector<int>& rem_coeffs = suffix_coeffs[pos];
int cnt = rem_nums.size();
sort(rem_nums.begin(), rem_nums.end());
long long min_rem = 0;
for (int i = 0; i < cnt; i++) {
min_rem += (long long)rem_nums[i] * rem_coeffs[i];
}
sort(rem_nums.rbegin(), rem_nums.rend());
long long max_rem = 0;
for (int i = 0; i < cnt; i++) {
max_rem += (long long)rem_nums[i] * rem_coeffs[i];
}
if (current_sum + min_rem > total_sum || current_sum + max_rem < total_sum) {
return false;
}
for (int num = 1; num <= n; num++) {
if (used[num]) continue;
used[num] = true;
ans[pos] = num;
long long new_sum = current_sum + (long long)num * coeff[pos];
if (dfs(pos + 1, new_sum)) {
return true;
}
used[num] = false;
}
return false;
}
int main() {
cin >> n >> total_sum;
vector<vector<int>> C_table(12, vector<int>(12, 0));
for (int i = 0; i < 12; i++) {
C_table[i][0] = 1;
for (int j = 1; j <= i; j++) {
C_table[i][j] = C_table[i-1][j-1] + C_table[i-1][j];
}
}
coeff.resize(n);
for (int i = 0; i < n; i++) {
coeff[i] = C_table[n-1][i];
}
suffix_coeffs.resize(n);
for (int i = 0; i < n; i++) {
vector<int> tmp;
for (int j = i; j < n; j++) {
tmp.push_back(coeff[j]);
}
sort(tmp.rbegin(), tmp.rend());
suffix_coeffs[i] = tmp;
}
ans.resize(n);
used.assign(n+1, false);
dfs(0, 0);
return 0;
}
DFS 搜索:
-
剪枝:计算剩余数字的最小和最大可能贡献,若当前和加上最小贡献超过
total_sum或加上最大贡献仍不足total_sum,则剪枝。 -
字典序保证:按数字 1 到 n 的顺序尝试放置,确保找到的第一个解字典序最小。
-
终止与输出:当放置完所有数字且和等于
total_sum时,输出序列并退出程序。
复杂度分析
时间复杂度
-
最坏情况:
在没有剪枝的情况下,需要遍历所有种排列。当
时,
,理论上可能超时。
-
实际运行(含剪枝):远低于
空间复杂度
- 显式存储:
- 组合数表:
(固定大小)
- 系数数组:
- 后缀系数表:
(
时最大 78 个整数)
- DFS 栈深度:
- 组合数表:
- 总空间:
时,总内存占用
KB,可视为常数空间。

京公网安备 11010502036488号