华为OD机试【乘坐保密电梯】(java)(200分)

1、题目描述

有一座保密大楼,你从 0 楼到达指定楼层 m,必须这样的规则乘坐电梯:
给定一个数字序列,每次根据序列中的数字 n,上升 n 层或者下降 n 层,前后两次的方向必须相反,规定首次的方向向上,自行组织序列的顺序按规定操作到达指定楼层。
求解到达楼层的序列组合,如果不能到达楼层,给出小于该楼层的最近序列组合。
说明:

  1. 操作楼层不限定楼层范围,楼层可以为负数。
  2. 必须对序列中的每个项进行操作,不能只使用一部分。
  3. 无论是上楼还是下楼,优先使用序列中尚未使用过的最大数字。

2、输入描述

第一行:期望的楼层,取值范围 [1,50];序列总个数,取值范围 [1,23]。
第二行:序列,每个值的取值范围 [1,50]。

3、输出描述

能够到达楼层或者小于该楼层最近的序列。
补充说明

  1. 操作楼层时不限定楼层范围;
  2. 必须对序列中的每个数操作,不能只使用一部分;
  3. 无论是上楼还是下楼,优先使用序列中未使用过的最大数字;
    用例:
输入
5 3
1 2 6

输出
6 2 1
ps:1 2 66 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);
}

执行结果如下:
在这里插入图片描述

  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

搬砖小夫子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值