- 1≤�≤12
- 1≤sum≤12345
解题思路
这题的关键是先看出"不断相加"后的结果,本质上是一个加权和。
以 �=4 为例:
a1 a2 a3 a4
a1+a2 a2+a3 a3+a4
a1+2a2+a3 a2+2a3+a4
a1+3a2+3a3+a4
最后一行的系数是 1, 3, 3, 1,正好对应杨辉三角的一行。
继续推广可知,最终结果可以写成:
∑�=1���⋅�(�−1,�−1)
于是原题就转化成了:
排列 �1,�2,...,��,找到一个字典序最小的方案,使得
∑�=1���⋅�(�−1,�−1)=sum
所以整道题可以分成两步:
- 先预处理出杨辉三角,得到每一位对应的权值。
- 再用 DFS 枚举排列,计算当前加权和。
因为题目要求输出字典序最小的解,所以搜索时按 1 ~ n 的顺序枚举。这样第一个满足条件的方案就是答案。
剪枝
设当前已经填到第 idx 位,当前加权和为 cursum。
如果 cursum > sum,后面再怎么填也只会更大,就可以直接返回。
复杂度分析
- 时间复杂度:最坏情况下为 �(�!⋅�)
需要枚举排列,搜索过程中每个状态的转移是常数级,整体主要由全排列数量决定。 - 空间复杂度:�(�2)
其中杨辉三角需要 �(�2),递归栈、路径数组和标记数组需要 �(�)。
n 的范围较小
代码实现
#define rep(i,a,b) for(int i=(int)a;i<int(b);++i)
int n, num;
int c[15][15]; // 杨辉三角
int coef[15]; // 每一位的系数
int path[15]; // 当前排列
bool used[15];
bool found;
void init() {
rep(i, 0, 13) {
c[i][0] = c[i][i] = 1;
rep(j, 1, i) {
c[i][j] = c[i - 1][j - 1] + c[i - 1][j];
}
}
}
void dfs(int idx, int cursum) {
if (found) return;
if (cursum > num) return;
if (idx == n) {
if (cursum == num) {
rep(i, 0, n) cout << path[i] << " ";
cout << '\n';
found = true;
}
return;
}
rep(i, 1, n + 1) {
if (used[i]) continue;
used[i] = true;
path[idx] = i;
dfs(idx + 1, cursum + i * coef[idx]);
used[i] = false;
}
}
void solve() {
init();
cin >> n >> num;
rep(i, 0, n) {
coef[i] = c[n - 1][i];
}
dfs(0, 0);
}
易错点
- 杨辉三角的预处理
- DFS的使用
- 要按从小到大的顺序枚举,才能保证第一个找到的是字典序最小解。
- 剪枝条件写成
cursum > sum,不要漏掉。