数组中两个数之和的最小差异

时间:2015-06-12 10:30:31

标签: c++ algorithm optimization dynamic-programming

我正在尝试解决此问题problem

  

物理学教授向班上的学生们提供了项目。学生必须组成一个由两人组成的团队来完成这个项目。教授让学生决定团队。班上的学生数量是均匀的。

     

每个学生都有知识水平。它告诉每个学生有多少知识。团队的知识水平是两个学生的知识水平的总和。

     

学生决定组建团队,使知识最高的团队与知识最低的团队之间的差异最小。

     

输入

     

输入的第一行将包含多个测试用例t;在接下来的t行中,第一个数字是n,班级中的学生数量,后跟n个整数,表示n个学生的知识水平

     

输出

     

您的输出应该是一行,其中包含知识最高的团队与知识最低的团队之间的最低差异。

解决方案归结为计算未排序数组中两个数之和的最小差异。到目前为止,我已经尝试过:

  1. 对数组进行排序。
  2. 在位置i和n-i-1处添加元素,并将其与位置i + 1和n-i处的元素之和进行绝对差异。
  3. 将差异存储在优先级队列中。
  4. 弹出优先级队列的顶部以获得答案。
  5. 而且,这是我尝试过的代码:

    #include <bits/stdc++.h>
    using namespace std;
    
    int main()
    {
        ios_base::sync_with_stdio(false);
    
        int T=0,num=0;
        cin >> T;
    
        while(T--){
            cin >> num;
            int *a = new int[num];
            for(int i = 0; i < num; i++){
                cin >> a[i];
            }
            sort(a,a+num);
            priority_queue<int> pq;
            for(int i = 0; i < num-2; i++){
                int j = i+1;
                pq.push(abs(a[i]+a[num-1-i]-a[j]-a[num-j-1]));
            }
            cout << pq.top()<< endl;
        }
        return 0;
    }
    

    此解决方案超出了时间限制,我的直觉是在某些情况下可能会失败。 描述和标签提示动态编程解决方案,但不知何故,我无法推断出最佳子结构。有人可以帮忙吗?

3 个答案:

答案 0 :(得分:3)

你是对的,可以通过将第一个元素与最后一个元素进行排序和配对来获得最佳排列。 (†)

步骤2以及您的方法中的后续内容是错误的。我们不需要优先级队列。我们只需要从创建的对中找到最大和最小总和。

伪代码:

Sort the array.
min = MAX_INT
max = 0
for (i = 0; i < n / 2; i++):
    sum = array[i] + array[n-i-1]
    if (min > sum) min = sum
    if (max < sum) max = sum
return max - min

(†)为什么这是真的?

让我们考虑包含1 2 3 6的排序数组。它看起来像这样:

      #
      #
      #
---------- average = 12 / 4 = 3
    # #
  # # #
# # # #

理想情况下,我们以这种方式对它们进行配对,差异为0.如果平均值为3,那么理想的平均对将为6。

嗯,我们不能这样做,我们可以在图像上清楚地看到它。我们可以在平均值以下的一个位置上将溢出3高于平均值。但是有2个这样的地方,大小为2和1。

如果我们填补可能的最大差距,那就更好了,所以首先在左边。通过这种方式,我们获得了7和5的总和,这有点偏离了平均值,但最低限度为1比1.任何其他配对都会相同或更差。

答案 1 :(得分:2)

首先,你是对的,迭代地将剩下的最好的学生与剩下最差的学生结合起来,给你最好的结果(见下面的证据)。

但是,您无法正确计算该解决方案的成本。您必须遍历所有组合对才能找到最佳和最差对的质量值,并且只有在找到它们之后,您才能减去它们的值。顺便说一句。您不需要优先级队列来查找最小值(它们被认为是更复杂的用例,您插入并弹出几个次,甚至更新队列中的值),简单的累加器变量({ {1}}和min)会这样做。假设数组max已经排序,代码可能如下所示:

a

现在我想证明为什么你选择的配对(迭代配对最好的剩余学生和最差的剩余学生)是最佳的,这在这里是必不可少的。如果这不成立,我们需要一个完整的其他算法。

我们通过证明不可能改进该配对给出的解决方案来证明这一点。

如果我们想要改进它,我们将不得不减少最佳和最差对之间的差异,这意味着要么降低最佳对,要么提高最差对(或两者)。

让我们首先说明为什么降低最佳配对是不可能的。

让我们给出对数指数:包含最高数字和最低数字的对将是p_1 =(a_1,b_1),具有下一个最高数字和下一个最低数字的对将是p_2 =(a_2,b_2)等等,直到p_n。例如:

int min = 2 * MAXIMUM_KNOWLEDGE_LEVEL;
int max = 2 * MINIMUM_KNOWLEDGE_LEVEL;
for (int i = 0; i < num / 2; i++) {
    int quality = a[i]+a[num-1-i];
    if (quality < min) {
        min = quality;
    }
    if (quality > max) {
        max = quality;
    }
}
int result = max - min;

现在其中一对,我们称之为p a b quality(p) p_1 = (a_1, b_1) = (10, 1) -> 11 (= min) p_2 = (a_2, b_2) = ( 9, 4) -> 13 p_3 = (a_3, b_3) = ( 8, 5) -> 13 p_4 = (a_4, b_4) = ( 8, 7) -> 15 (= max) p_5 = (a_5, b_5) = ( 7, 7) -> 14 (1&lt; = m&lt; = n)将是最大对。如果我们想降低最大值,我们将不得不打破这一对。但是我们可以将p_m = (a_m, b_m)a_m配对,所以新配对的总和会更少?我们需要找到一个b,我们称之为b_y低于b_m(否则这不会有任何改进)。我们只能通过向上列表找到较低的b_y,即y < m。但同样适用于所有成对的对象:如果我们进一步向上(p_xx < m),并尝试为旧b_y找到新的合作伙伴a_x ,我们必须考虑a_x >= a_m,这使y的某些选择变得不可能。如果我们选择y>=m,则意味着b_y >= b_m,因此我们希望避免quality(a_x, b_y) = a_x + b_y >= a_m + b_y >= a_m + b_m = quality(p_m)。因此y必须低于m。如果我们考虑到该限制,则m a {a_1,...,a_m}可能的值只有m-1个可能的合作伙伴{b_1,...b_(m-1)},因此配对是不可能的。因此,降低最佳对的价值是不可能的。

在上面的示例中:为了减少最大值15,所有对都必须具有低于15的值。这意味着对的所有左侧值都高于或等于最大对(8, 8,9,10)需要低于最大对的右侧伙伴(7)的合作伙伴,因此只能使用1,4和5。

4 "a"s are fighting for only 3 "b"s

证明提出最差对的证据不可能与反向比较运算符相同,并且切换ab(在这种情况下,我们有太多b s a s}太少了。

答案 2 :(得分:0)

如果在排序数组之后开始添加两端的值(从头到尾,依此类推),只存储最大和最小结果而不是队列,这可能会有所帮助。然后你可以通过从最大值中减去min来获得答案

Example: 4 2 6 4 3 1
1. Sort: 1 2 3 4 4 6
2. Iterate:
a. currentValue = a[0] + a[5] = 7; max = min = currentValue = 7
b. currentValue = a[1] + a[4] = 6; max = 7; min = 6
c. currentValue = a[2] + a[3] = 7; max = 7; min = 6
3. Obtain result: difference = max - min = 7 - 6 = 1 

解决方案的简短证明: 在排序之后,可以以这种方式成像a&lt; = b&lt; = c ...&lt; = k&lt; = n。我们假设现在所有的值都是不同的,所以a&lt; b&lt; c&lt; ...&lt; k&lt; ñ。如果值不同,则(b-a> = 1)因此(b => a + 1)以及(n => k + 1)。对于a + n和b + k,差值将至少为(a + n) - (b + k)=(a + n) - (a + 1 + n-1)= 0。对于a + k和b + n,差值将至少为(b + n) - (a + k)=(a + 1 + n) - (a + n-1)= 2。这可以扩展为&lt; = c等等。 如果某些值相同,则替换将无效。