问题描述
给定一个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
题意分析:划重点 “排列”二字,一般规定排列是没有重复的数字的,数据范围是0到10,果断dfs全排列搜索 ,将0-n的数字进行全排列,用数组res来保存,然后当排列完成之后,对数组里的元素进行题目中的数字“操作”,从第n行变成一行,最后检查一下结果是不是相等就可以。
注意:这里搜出来的结果可能有很多,题目是要求字典序排序,就是在都能满足题意的情况下,将数字小的输出,比如3 1 2 4和3 2 1 4,
就选择输出3 1 2 4
代码如下:
#include <iostream>
using namespace std;
const int N = 15;
int res[N];
int flag[N];//判重数组
int n,sum;
int is_end = 0; //结束循环的标志,即保证只输出一个结果
bool check()
{
int tmp[N];
for(int i = 0;i<n;i++)
tmp[i] = res[i];
int count = n;
int j = 1;//限定合并的范围
while(--count)
{
for(int i = 0;i<n-j;i++)//如n=4,j=1就只合并前三项
tmp[i] = tmp[i]+tmp[i+1];
j++;
}
if(tmp[0] == sum)
return true;
return false;
}
int dfs(int u)
{
if(u == n&&check())//当u==n的时候,表示排序完成,进行检查(u从0开始)
{
is_end = 1;//表示找到最小字典序,可以结束
for(int i = 0;i<n;i++)
printf("%d ",res[i] );
printf("\n");
}
for(int i = 1;i<=n;i++)
{
if(is_end)
break;
if(!flag[i])
{
res[u] = i;
flag[i] = 1;
dfs(u+1);
flag[i] = 0;//恢复现场
}
}
}
int main()
{
cin>>n>>sum;
dfs(0);
return 0;
}