问题描述
给定一个1~N的排列a[i],每次将相邻两个数相加,得到新序列,再对新序列重复这样的操作,显然每次得到的序列都比上一次的序列长度少1,最终只剩一个数字。
例如:
3 1 2 4
4 3 6
7 9
16
现在如果知道N和最后得到的数字sum,请求出最初序列a[i],为1~N的一个排列。若有多种答案,则输出字典序最小的那一个。数据保证有解。
输入格式
第1行为两个正整数n,sum
输出格式
一个1~N的一个排列
样例输入
4 16
样例输出
3 1 2 4
数据规模和约定
0<n<=10
此方法通过找规律找到每位上的数乘积满足杨辉三角,然后用dfs进行全排列,因为全排列满足从小到大的特点,即找到的第一中符合要求的组合就是字典数最小的组合,即答案。
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 11;
int mul[N]; // 第n层的杨辉三角
int n, sum;
bool st[N];
int path[N], c[N][N], cnt = 0;
void dfs(int x) // x表示遍历到了数的第x位
{
if(x == n) // 当遍历完最后一位 (path数组从0开始)
{
int all = 0;
for (int i = 0; i < n; i++)
all += path[i] * mul[i];
if (all == sum && !cnt) // 找到的第一个正确组合就是答案
{
for (int i = 0; i < n; i++)
cout << path[i] << ' ';
cnt++ ;
return ;
}
}
for(int i = 1; i <= n; i++ )
{
if(!st[i])
{
path[x] = i;
st[i] = true;
dfs(x + 1); // 进行下一位填充
// 回溯 (要还原现场)
st[i] = false;
path[x] = 0;
}
}
}
int main()
{
cin >> n >> sum;
for(int i = 0; i < n; i++ )
for(int j = 0; j <= i; j++ )
if(!j) c[i][j] = 1;
else c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]); // 递推式 : 包含 和 不包含
// C[i][j] 表示组合数即 从i个物品中选j个物品的方案数
// 第n层杨辉三角满足 C[n - 1][0]、C[n - 1][1]、C[n - 1][2] ...
for(int i = 0; i < n; i++ )
mul[i] = c[n - 1][i];
dfs(0);
return 0;
}