1、题目描述
有一座保密大楼,你从 0 楼到达指定楼层 m,必须这样的规则乘坐电梯:
给定一个数字序列,每次根据序列中的数字 n,上升 n 层或者下降 n 层,前后两次的方向必须相反,规定首次的方向向上,自行组织序列的顺序按规定操作到达指定楼层。
求解到达楼层的序列组合,如果不能到达楼层,给出小于该楼层的最近序列组合。
说明:
- 操作楼层不限定楼层范围,楼层可以为负数。
- 必须对序列中的每个项进行操作,不能只使用一部分。
- 无论是上楼还是下楼,优先使用序列中尚未使用过的最大数字。
2、输入描述
第一行:期望的楼层,取值范围 [1,50];序列总个数,取值范围 [1,23]。
第二行:序列,每个值的取值范围 [1,50]。
3、输出描述
能够到达楼层或者小于该楼层最近的序列。
补充说明
- 操作楼层时不限定楼层范围;
- 必须对序列中的每个数操作,不能只使用一部分;
- 无论是上楼还是下楼,优先使用序列中未使用过的最大数字;
用例:
输入
5 3
1 2 6
输出
6 2 1
ps:1 2 6 和 6 2 1 都满足条件,按照先处理大值的规则,输出结果为 6 2 1
上6下2上1
温馨提示!!!
华为OD机试考试官方会对考生代码查重。华为od机试因为有题库所以有很大的概率抽到原题。如果碰到了题库中的原题,千万不要直接使用题解中的代码,一定要做些修改,比如代码中的变量名,除此之外,代码的组织结构和逻辑也要进行一些改变,所以在日常的刷题中,要提前编写好属于自己的代码。
4、题解
本题比较难,若考试遇到针对自身情况若拿不到满分,争取在时间允许的范围内拿到部分分数
首先将输入的数字序列排序,计算目标值,当n为偶数时,加法数和减法数量相等 m=n/2,当n为奇数时,加法数量=n/2+1,减法数量=n/2(首次必须向上,所以奇数时加法数多1)
然后,获取加法数集合,返回值为加法数集合之和,遍历输入集合,若当前加法数之和是否大于目标值,移除已经计算过的数字
最后,获取到达楼层的序列组合并输出
// 加法数集合
private static List<Integer> addNumList = new ArrayList<>();
// 输入数字集合
private static List<Integer> arrList = null;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int[] param = Arrays.stream(sc.nextLine().split(" "))
.mapToInt(Integer::parseInt).toArray();
int floor = param[0];
int num = param[1];
// 楼层序列
Integer[] arr = Arrays.stream(sc.nextLine().split(" "))
.map(Integer::parseInt).toArray(Integer[]::new);
// 降序
Arrays.sort(arr, (a, b) -> b - a);
arrList = Arrays.stream(arr).collect(Collectors.toList());
int sum = 0;
for (int i = 0; i < arr.length; i++) {
sum += arr[i];
}
sum += floor;
/**
* n为偶数->加法数和减法数量相等 m = n/2;
* n为奇数->加法数量 = n/2+1 减法数量=n/2
*/
int m = arr.length / 2;
if (arr.length % 2 == 1) {
m++;
}
// 目标值
int target = sum / 2;
// 获取加法数集合之和
getAddNum(0, m, target);
// 获取到达楼层的序列组合
List<Integer> resList = new ArrayList<>();
for (int i=0; i<addNumList.size(); i++) {
resList.add(addNumList.get(i));
if (i < arrList.size()) {
resList.add(arrList.get(i));
}
}
// 输出到达楼层的序列组合
StringBuilder sb = new StringBuilder();
for (int i=0; i<resList.size(); i++) {
sb.append(resList.get(i)).append(" ");
}
String result = sb.deleteCharAt(sb.length() - 1).toString();
System.out.println(result);
}
/**
* 获取加法数集合
* @param curSum 当前加法数之和
* @param m 加法数集合的数字个数
* @param target 目标值
* @return 加法数集合之和
*/
private static int getAddNum(int curSum, int m, int target) {
if (addNumList.size() == m && curSum <= target) {
return curSum;
}
// 最终加法数之和
int addSum = 0;
// 加法数集合
List<Integer> tmpAddNumList = new ArrayList<>();
List<Integer> copyTmpList = new ArrayList<>();
for (int i = 0; i < arrList.size(); i++) {
// 获取当前数
int num = arrList.get(i);
if (curSum + num > target) {
continue;
}
addNumList.add(num);
// 移除计算过的数
arrList.remove(i);
int tmp = getAddNum(curSum + num, m, target);
if (tmp == -1) {
addNumList.remove(addNumList.size() - 1);
arrList.add(i, num);
continue;
}
if (tmp == target) {
return target;
} else if (tmp > addSum && tmp < target) {
addSum = tmp;
transfer(tmpAddNumList, addNumList);
transfer(copyTmpList, arrList);
}
addNumList.remove(addNumList.size() - 1);
arrList.add(i, num);
}
if (addSum == -1) {
return -1;
}
transfer(addNumList, tmpAddNumList);
transfer(arrList, copyTmpList);
return addSum;
}
public static void transfer(List<Integer> target, List<Integer> source) {
target.clear();
target.addAll(source);
}
执行结果如下: