【华为OD题库-039】乘坐保密电梯-java

题目

有一座保密大楼,你从0楼到达指定楼层m,必须这样的规则乘坐电梯:给定一个数字序列,每次根据序列中的数字n上升n层或者下降n层,前后两次操作的方向必须相反,规定首次的方向向上,自行组织序列的顺序按规定操作到达指定楼层。求解到达楼层的序列组合,如果不能到达楼层,给出小于该楼层的最近序列组合。
说明:
操作电梯时不限定楼层范围
必须对序列中的每个项进行操作,不能只使用—部分。
输入描述:
第一行:期望的楼层,取值范围[1,50];序列总个数,取值范围[1,23]
第二行:序列,每个值取值范围[1,50]
输出描述
能够达到楼层或者小于该楼层最近的序列
补充说明:
操作电梯时不限定楼层范围
必须对序列中的每个项进行操作,不能只使用—部分
示例1
输入:
5 3
1 2 6
输出:
6 2 1
说明:
1 2 6
6 2 1
均为可行解,按先处理大值的原则结果为6 2 1

思路

排列组合题,此类问题思路可参考:【JAVA-排列组合】一个套路速解排列组合题
此题来说,有两种思路解题:

  1. 排列思路
  2. 组合思路

排列思路

因为题目说明中有"优先处理大值原则",比如9 8 5 1和9 5 5 4两组数的和相等,按照大值原则应该选择9 8 5 1。所以可以先将原数组从大到小排序,这样在计算排列的时,会优先选择大的数
然后求其全排列,第一个为正,第二个为负…,记录一组排列的和
找到最符合条件的和的组合:
当存在组合的总和与目标值相等时,如果只有一个,直接输出该组合;如果存在多个时,输出第一个即可。(优先输出较大值。因为已经按倒序排列,所以第一个相等的组合一定是优先输出的。)
当不存在组合的总和等于目标值时,找到最接近的总和(只能比目标值小),
输出结果

组合思路

排列的方法有很多冗余计算,效率较低。可以考虑将问题转为组合问题。
设序列和为sum,正数和为positive,负数和为neg,目标为target,那么:
因为:positive+neg=sum;positive-neg<=target
所以:positive<=(sum+target)/2
考虑数组总数为奇数的情况,由于第一个是正数,那么其正数的数量应该为,(nums.length+1)/2;负数的数量为:nums.length/2
所以原来的题目就转化为,在nums中找(nums.length+1)/2个数,使其和<=(sum+target)/2,求sum最大时的组合是多少,存在多个时,按照大值原则输出。
关键在于此种情况存在多个值时,应该怎么判断取哪一个?

比如,nums:4 5 2 5 9 8 5 1,target:7
可能能找到的整数组合为:9 8 5 1或者9 5 5 4两者和都为23,如果取前者,那么8是正数,否则8为负数。组装答案时必须先正后负的形式,前者最符合的情况是9 X 8…,后者最符合的情况是 9 8 5…,很明显后者更符合情况。
所以当两组数同时符合时,判断逻辑应该是根据奇偶位判断,如果是奇数位,越大越好,如果是偶数位,越小越好(越小,说明能够留给负数的值更大,可以排在前面)

根据组合思路,可以找到原nums中应该标记正数的索引集合。从而区分出nums哪些为正,哪些为负
最后,根据正负数集合,先后交替从正、负数集合选择值到最后的结果即可。

题解

排列

package hwod;

import java.util.*;
import java.util.stream.Collectors;

public class SecurityElevator {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int target = sc.nextInt();
        int n = sc.nextInt();
        sc.nextLine();
        int nums[] = new int[n];
        for (int i = 0; i < n; i++) {
            nums[i] = sc.nextInt();
        }
        int[] res = securityElevator(nums, target);
        for (int i = 0; i < res.length; i++) {
            if (i != 0) System.out.print(" ");
            System.out.print(res[i]);
        }

    }

    private static int[] res;
    private static int nearest = Integer.MAX_VALUE;

    private static int[] securityElevator(int[] nums, int target) {
        Arrays.sort(nums);
        LinkedList<Integer> path = new LinkedList<>();
        int[] used = new int[nums.length];
        res = new int[nums.length];
        dfs(nums, used, path, target);
        return res;
    }

	//排列思路解题
    private static void dfs(int[] nums, int[] used, LinkedList<Integer> path, int target) {
        if (path.size() == nums.length && target >= 0) {
            if (target < nearest) {
                res = path.stream().mapToInt(i -> i).toArray();
                nearest = target;
            }

        }

        for (int i = nums.length - 1; i >= 0; i--) {
            if (used[i] == 1) continue; //不能选择自身
            path.addLast(nums[i]);
            used[i] = 1;
            int flag = (path.size() & 1) == 1 ? 1 : -1;
            dfs(nums, used, path, target - (nums[i] * flag));
            path.removeLast();
            used[i] = 0;
        }
    }
}

组合

package hwod;

import java.util.*;
import java.util.stream.Collectors;

public class SecurityElevator {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int target = sc.nextInt();
        int n = sc.nextInt();
        sc.nextLine();
        int nums[] = new int[n];
        for (int i = 0; i < n; i++) {
            nums[i] = sc.nextInt();
        }
        int[] res = securityElevator2(nums, target);
        for (int i = 0; i < res.length; i++) {
            if (i != 0) System.out.print(" ");
            System.out.print(res[i]);
        }

    }

    private static int[] res;
    private static int nearest = Integer.MAX_VALUE;
    private static int[] securityElevator2(int[] nums, int target) {
        int sum = Arrays.stream(nums).sum();
        //neg >=(sum - target) / 2
        //选一半的数,使和<=pos
        //neg+positive=sum,positive-neg<=target;2*pos-sum<=target,pos<=(sum+target)/2
        int positive = (sum + target) / 2;
        Arrays.sort(nums);
        LinkedList<Integer> path = new LinkedList<>();
        int[] used = new int[nums.length];
        //得到正数的索引集合res
        dfs2(nums, nums.length - 1, path, used, positive);
        List<Integer> positiveIdxList = Arrays.stream(res).boxed().collect(Collectors.toList());
        //标记原数组的正负号
        int[] positives = new int[(nums.length + 1) / 2];
        int[] negatives = new int[nums.length / 2];
        int m = 0, n = 0;
        for (int i = nums.length - 1; i >= 0; i--) {
            if (positiveIdxList.contains(i)) {
                positives[m++] = nums[i];
            } else {
                negatives[n++] = nums[i];
            }
        }
        //组装正负数,得到答案,先正后负
        int[] ans = new int[nums.length];
        for (int i = 0; i < nums.length; i++) {
            if((i&1)==0)ans[i] = positives[i / 2];
            else ans[i] = negatives[i / 2];
        }


        return ans;
    }

    //组合思路解题,在nums中找(nums.length+1)/2个数,使其和<=(sum+target)/2;
    private static void dfs2(int[] nums, int start, LinkedList<Integer> path, int[] used, int target) {

        if (target < 0) return;
        if (path.size() == (nums.length + 1) / 2) {
            if (target < nearest) {
                res = path.stream().mapToInt(i -> i).toArray();
                nearest = target;
            } else if (target == nearest) {
                res = getMoreBigger(res, path.stream().mapToInt(i -> i).toArray(), nums);
            }
            return;

        }
        for (int i = start; i >= 0; i--) {
            if (i < start - 1 && nums[i] == nums[i + 1] && used[i + 1] == 0) continue;
            path.addLast(i);
            used[i] = 1;
            dfs2(nums, i - 1, path, used, target - nums[i]);
            path.removeLast();
            used[i] = 0;
        }
    }

    private static int[] getMoreBigger(int[] arr1, int[] arr2, int[] nums) {
        int i = 0;
        int size = arr1.length;
        while (i < size && arr1[i] == arr2[i]) {
            i++;
        }
        if (i >= size) return arr1;
        if ((i & 1) == 0) return nums[arr1[i]] > nums[arr2[i]] ? arr1 : arr2;
        else return nums[arr1[i]] > nums[arr2[i]] ? arr2 : arr1;
    }
}

推荐

如果你对本系列的其他题目感兴趣,可以参考华为OD机试真题及题解(JAVA),查看当前专栏更新的所有题目。

说明

本专栏所有文章均为原创,欢迎转载,请注明文章出处:https://blog.csdn.net/qq_31076523/article/details/134176793。百度和各类采集站皆不可信,搜索请谨慎鉴别。技术类文章一般都有时效性,本人习惯不定期对自己的博文进行修正和更新,因此请访问出处以查看本文的最新版本。

  • 20
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
华为OD华为公司为了提高工作效率和保护商业机密而研发的一款保密电梯。下面我将从技术、安全和隐私保护三个方面回答关于乘坐保密电梯华为OD的问题。 首先,华为OD采用了最先进的技术,其核心是对电梯系统进行全面加密保护。只有经过身份验证的人员才能进入电梯,并且可以根据不同人员的权限设置不同的楼层进入权限。同时,电梯内部设有高清摄像头和声音感应器,可以实时监控并录制电梯内的情况,以便及时处理任何安全事件。 其次,华为OD还具有高度的安全性。电梯采用双重验证的方式,即需要刷卡和输入密码才能激活电梯系统。此外,电梯配备了智能感应门,只有身份验证合法的人员在正确的时间段内才能进入电梯。这些安全措施有效防止了未经授权人员进入电梯,确保了企业机密和员工的安全。 最后,华为OD注重隐私保护。电梯内部设有隐私模式开关,员工在进入电梯并刷卡通过后,可以选择开启该开关,使电梯内的监控设备失效,保护员工的隐私。此外,电梯内还配备了紧急报警按钮和紧急呼叫功能,员工可以在遇到突发情况时通过这些功能及时求助。 综上所述,乘坐华为OD保密电梯可以获得全面的技术支持、高度的安全性和隐私保护。华为OD致力于为企业提供最安全可靠的工作环境,在保护商业机密和员工安全的同时提高工作效率。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值