交换两个序列的元素,使得元素和的差异变得最小。

Posted

技术标签:

【中文标题】交换两个序列的元素,使得元素和的差异变得最小。【英文标题】:Swap the elements of two sequences, such that the difference of the element-sums gets minimal. 【发布时间】:2012-01-28 19:29:00 【问题描述】:

面试题:

给定两个无序整数序列ab,它们的大小为n,所有 数字随机选择:交换ab的元素,使得a的元素之和减去b的元素之和最小。

举个例子:

a = [ 5 1 3 ]
b = [ 2 4 9 ]

结果是 (1 + 2 + 3) - (4 + 5 + 9) = -12。

我的算法:将它们排序在一起,然后将第一个最小的 n 整数放入 a 并留在 b 中。它在时间上是 O(n lg n),在空间上是 O(n)。我不知道如何将其改进为时间 O(n) 和空间 O(1) 的算法。 O(1) 意味着我们不需要更多额外的空间,除了 seq 1 和 2 本身。

有什么想法吗?

另一个问题是:如果我们需要最小化差异的绝对值(最小化|sum(a) - sum(b)|)怎么办?

Python 或 C++ 思维者优先。

【问题讨论】:

听起来像是功课。如果是这样,请相应地标记。 如果考虑原始的 a 和 b 列表,它在空间中不可能是 O(1)。如果您不考虑它们,则只需直接交换值。无论哪种情况,请在问题中提供更多详细信息。 @GaretJax,如何在 O(n) 时间内高效地进行交换? 只需对单个元素使用单个临时变量(O(1) 空间)并遍历列表(O(n) 时间)。 -1 “交换 a 和 b 的元素”非常模糊——我在答案中发现了大约 2.01 种对此的理解。 【参考方案1】:

修改后的解决方案:

    合并两个列表 x = merge(a,b)。

    计算 x 的中位数(复杂度 O(n) 见http://en.wikipedia.org/wiki/Selection_algorithm)

    在 a 和 b 之间使用这个中值交换元素。即在a中找到一个小于中位数的元素,在b中找到一个大于中位数的元素并交换它们

最终复杂度:O(n)

最小化绝对差是NP完全的,因为它等价于背包问题。

【讨论】:

您能解释一下等效性吗?我似乎很清楚,OP 的排序解决方案并将最小值放入 a 将最小化 sum(a)-sum(b):我错过了什么? 你说的是第二部分(最小化绝对值)还是两者兼而有之?因为我认为它不适用于第一个,所以要获得最大的负差,将 n/2 最低的数字放在一个列表中,将 n/2 最高的数字放在另一个列表中,正如 OP 所说。 @DSM 我以为你在计算绝对最小值。如果只是最低限度使用新的解决方案。无需排序:) 对于中位数,您必须对 x 进行排序!如果你对 x 进行排序,复杂度不是 O(n)。 @ChristianAmmer 可以在 O(n) 中找到中位数。见:en.wikipedia.org/wiki/Selection_algorithm【参考方案2】:

我想到的是以下算法大纲:

    C = A v B 对 C 的 #A(A 的数量)元素进行部分排序 从 C 中前 #A 元素的总和中减去 C 中最后 #B 元素的总和。

您应该注意到,您不需要对所有元素进行排序,只需找到 A 最小元素的数量即可。你给出的例子:

    C = 5, 1, 3, 2, 4, 9 C = 1、2、3、5、4、9 (1 + 2 + 3) - (5 + 4 + 9) = -12

C++ 解决方案:

#include <iostream>
#include <vector>
#include <algorithm>

int main()

    // Initialize 'a' and 'b'
    int ai[] =  5, 1, 3 ;
    int bi[] =  2, 4, 9 ;
    std::vector<int> a(ai, ai + 3);
    std::vector<int> b(bi, bi + 3);

    // 'c' = 'a' merged with 'b'
    std::vector<int> c;
    c.insert(c.end(), a.begin(), a.end());
    c.insert(c.end(), b.begin(), b.end());

    // partitially sort #a elements of 'c'
    std::partial_sort(c.begin(), c.begin() + a.size(), c.end());

    // build the difference
    int result = 0;
    for (auto cit = c.begin(); cit != c.end(); ++cit)
        result += (cit < c.begin() + a.size()) ? (*cit) : -(*cit);

    // print result (and it's -12)
    std::cout << result << std::endl;

【讨论】:

如果我们想要最优解(即 N/2 个最小值),那不是 O(N),而是 O(N log N/2)。 @Voo:你是对的,但是否存在 O(N) 算法——我想不出任何算法?而且我认为中位数的解决方案也不是 O(N),为了获得中位数,必须对序列进行排序,否则你是从中间挑选一个随机元素(或者我错了)? 哦,我同意 O(N log N/2) 似乎是最好的解决方案。毕竟我们需要 N/2 个最小值,我真的不知道在 O(N) 中这怎么可能。

以上是关于交换两个序列的元素,使得元素和的差异变得最小。的主要内容,如果未能解决你的问题,请参考以下文章

量数组交换差最小算法

[微软]有两个序列a,b,大小都为n,序列元素的值任意整数,无序; 要求:通过交换a,b中的元素,使[序列a元素的和]与[序列b元素的和]之间的差最小_利用排列组合思路解决_python版

选择排序

codeforces 594

序列和的前n小元素

leetcode困难2163删除元素后和的最小差值