【问题标题】:Split array into two even sets of integers, each so that both sets add up to the same number将数组拆分为两组偶数整数,每组整数加起来相同
【发布时间】:2021-12-05 10:16:33
【问题描述】:

我在面试时遇到了这个问题,似乎无法思考如何解决它。我还没有找到任何解释其背后逻辑的教程。

使用函数ArrayChallenge(arr),获取存储在arr 中的整数数组,该数组总是包含偶数个整数,并确定如何将它们分成两个偶数集,然后返回第一个的字符串表示set 后跟第二个集合,每个整数用逗号分隔,并且两个集合都按升序排序。最先出现的集合是第一个整数最小的集合。

例如如果 arr 是 [16,22,35,8,20,1,21,11],那么你的程序应该输出 1,11,20,35,8,16,21,22

[16,22,35,8,20,1,21,11] 总和 = 134

1,11,20,35 的总和是 = 67 8,16,21,22 之和 = 67

两个数组的大小也等于 arr.length /2

【问题讨论】:

  • 无论如何都要对数组进行排序。您通常需要排序的大多数数组问题。排序后,第一个和最后一个元素在一个列表中,而第二个和倒数第二个元素在另一个列表中。中间的 4 个数字让我感到困惑,因为当我把它写出来时,我认为它都是偶数放置在一个列表中,而所有奇数放置在另一个列表中,但这是不对的,尽管

标签: java arrays knapsack-problem partition-problem


【解决方案1】:

您将使用迭代并首先遍历数组。然后你做2个整数。 在每个迭代周期中,您首先检查 integer1 是否大于 integer2。然后将一个数字放入 array1 并将其值添加到 integer1。重复。 如果 int1 大于 int2,则将其放入 array2 并将值添加到 int2。 最后,对数组进行排序,你就完成了。 这就是我将如何解决它。这行得通吗?我真的很感兴趣。

【讨论】:

    【解决方案2】:

    问题不需要按时间顺序编码。制定一个集中程序来解决这个背包问题,但不要编写代码。然后对结果进行排序,给出结果。

    现在,如果您无法解决,比如超时,仍然有一种方法可以完成。

    这个问题可以进一步简化,通过使用数组arr的和,除以2,然后搜索这个半和的子数组。

    这个问题带来了一些奇怪的限制:arr 保持偶数个值 (8),两个结果数组应该有相同的偶数个值(都是 4 个)。

    选择第i值属于哪个子数组是二元的。

    所以从一个排序数组开始,到一半的时候把解删掉。

    您可以从 00001111(位 1 的一半)开始,这可能太大了,以下位将是 00010111、00011011、00011101、00011110、00101110、...

    简单的递归可能更简单,最多计数一半:

    // Array arr sorted decreasingly to have less values to try out.
    boolean solve(Set<Integer> selectedNumbers, int selectedSum, int index) {
        if (selectedNumbers.size() >= arr.length/2) {
            return sum == arrSum/2;
        }
        if (index > arr.length) {
            return false;
        }
        boolean solved = false;
    
        // First case: add array element at this index:
        if (selectedSum + arr[index] <= arrSum/2) {
            seplectedNumbers.add(arr[index]);
            arrSum += arr[index];
            solved = solve(selectedNumbers, arrSum, index + 1);
            if (!solved) {
                // No remove(int index), so remove an Object, Integer.
                selectedNumbers.remove(Integer.valueOf(arr[index]));
                arrSum -= arr[index];
            }
        }
    
        // Second case: do not add array element at this index:
        if (!solved) {
            solved = solve(selectedNumbers, arrSum, index + 1);
        }
        return solved;
    }
    

    以上当然是蛮力解决方案。如果您从事运筹学,您可能会发现这些数字的分布(如提到的位)。但是需要时间,对我来说,我微薄的数学知识会阻止这种情况。 解决后,如果您知道更快的解决方案,您可以发表评论。

    【讨论】:

    • 另一个改进是检查 discardedSum &gt; arrSum/2 是否(在第二种情况分支中增加),因为两种方式都可能出错 - 如果您丢弃的总数太高,您将找不到沿着这条路径保持平衡。否则,我怀疑这可以大大改善。
    • @tucuxi 作为审阅者,我会关注do; recurse; undo 之类的内容以及remove(int) 之类的陷阱。但是discardedSum &gt; ... 剪掉很好。我的代码也看起来不好。
    • 我的意思是算法。是的,你的代码很乱,不能编译——但你已经知道了 :-)
    • @tucuxi 我敢肯定这不是这里的情况,但我遇到了几个通过谷歌搜索工作的人,所以我肯定不会为过于优雅的代码而努力。不过看到你的回答会很高兴。另一个时间。
    • 我花时间写了一个答案 - 并避免插入和删除到(相当于)selectedNumbers,这应该会显着加快速度。
    【解决方案3】:

    这个答案在精神上与Joop Eggen 的答案相似。它实现了检查selectedTotal discardedTotal 的(小)优化以中止分支,如果它超过目标(请注意,这假定所有值都是正数;如果不是这种情况,最小值是 x &lt; 0,您只需将 -x 添加到所有值,运行算法,然后从答案中减去 -x

    输出与原始帖子中指定的完全相同 - 由于它仅在找到完整解决方案时生成,因此此代码应该比不断添加和删除值的算法更快部分答案,例如 Joop 的(selectedNumbers.add 后跟 selectedNumbers.remove,结果证明它不起作用;设置操作可能很快,但不执行它们甚至更快!)。

    public class Main {
        public static boolean search(int[] values, int goal, int index,
                                    int selectedTotal, int discardedTotal,
                                    List<Integer> selected, List<Integer> discarded) {
            if (selected.size() == values.length/2 &&
                    discarded.size() == values.length/2) {
                return selectedTotal == goal;
            }
            if (selectedTotal > goal ||
                    discardedTotal > goal ||
                    index == values.length) {
                return selectedTotal == goal;
            }
    
            // try selecting value at index ...
            if (selected.size() < values.length/2 &&
                    search(values, goal, index + 1,
                        selectedTotal + values[index], discardedTotal,
                        selected, discarded)) {
                selected.add(values[index]);
                return true;
            }
    
            // ... and, if that did not work, try discarding value at index
            if (discarded.size() < values.length/2 &&
                    search(values, goal, index + 1,
                        selectedTotal, discardedTotal + values[index],
                        selected, discarded)) {
                discarded.add(values[index]);
                return true;
            }
            return false;
        }
    
        public static List<Integer> solve(int[] values) {
            Arrays.sort(values);
            int goal = IntStream.of(values).sum() / 2;
            List<Integer> selected = new ArrayList<>();
            List<Integer> discarded = new ArrayList<>();
            if ( ! search(values, goal, 0,
                    0, 0, selected, discarded)) {
                throw new IllegalArgumentException("This puzzle cannot be solved");
            }
            Collections.reverse(selected);
            Collections.reverse(discarded);
            selected.addAll(discarded);
            return selected;
        }
    
        public static void main(String[] args) {
            System.out.println(solve(new int[] {16,22,35,8,20,1,21,11}));
        }
    }
    

    【讨论】:

    • 感谢您的回复。该解决方案可以找到两个总和相等的子数组。但是这两个子数组的大小不是给定数组的一半。数组必须被分成两个相等的子数组,每个子数组是 = to arr.lenght/2;
    猜你喜欢
    • 2016-11-28
    • 1970-01-01
    • 1970-01-01
    • 2012-09-06
    • 2011-12-28
    • 2012-01-19
    • 1970-01-01
    • 2010-12-26
    • 1970-01-01
    相关资源
    最近更新 更多