问题描述
给定一个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
题目解读:
1.对1~N的序列进行相邻两两相加,作为一个新序列,直到最后的序列只剩一个数
2.输入N和sum,要求输出字典序最小,且满足要求的原始序列
解决思路:
1.显然原始序列中只含有1~N(闭区间)且每个数字只出现一次,那么很容易想到,只要把1~N的全排列从小到大(满足字典序最小要求)枚举一次,然后挑出符合要求的序列输出即可
2.全排列用dfs算法,模板网上很多,随便在B站搜一下就行
3.“对全排列进行相邻两两相加,作为一个新序列,直到最后的序列剩下的最后一个数等于sum“即为符合要求的序列,为了使代码简洁方便,可以创建一个judge()实现
代码实现:
import java.util.Scanner;
public class Main {
static int[] arr= {1,2,3,4,5,6,7,8,9,10};
static boolean[] book=new boolean[11];//用来标记数字是否用过
public static void main(String[] args) {
Scanner scanner=new Scanner(System.in);
int n=scanner.nextInt();
int sum=scanner.nextInt();
int[] copy=new int[11];//全排列后得到的数组
dfs(0,n,sum,copy);
}
//得到按字典排序的全排列
public static void dfs(int k,int n,int sum,int[] copy) {
if(k==n&&judge(copy, n, sum)) {
print(copy,n);
System.exit(0);//正常终止程序(题目要求只输出字典序最小的)
}
for(int i=0;i<n;i++) {//遍历每一个数字
if(!book[i]) {//i没有用过
copy[k]=arr[i];//将arr[i]存入到copy数组中
book[i]=true;//标记i已经用过
dfs(k+1, n, sum,copy);
book[i]=false;//回溯
}
}
}
//判断得到的全排列是否符合条件
public static boolean judge(int[] copy, int n, int sum) {
int[] result=new int[n];
for(int k=0;k<n;k++) {
result[k]=copy[k];
}
while(n!=1) {
for(int j=0;j<n-1;j++) {
result[j]=result[j]+result[j+1];//相邻两两相加
}
n--;//每相加一轮,数组大小相应减1
}
if(result[0]==sum) {//符合要求,返回true
return true;
}
return false;
}
//输出全排列数组
private static void print(int[] copy,int n) {
for(int i=0;i<n;i++) {
System.out.print(copy[i]+" ");
}
System.out.println();
}
}