【华为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 = 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 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),查看当前专栏更新的所有题目。

你可能感兴趣的:(华为OD题库JAVA题解,java,华为od,排列组合)