【问题标题】:Arrange array so adjacent has less space that gives minimum sum排列数组,使相邻的空间更小,总和最小
【发布时间】:2022-01-03 16:25:41
【问题描述】:

我有一个偶数大小的数组,这是我的任务:

a) 丢弃数组中的任意 2 个元素。 b) 然后对元素进行配对,并计算配对中元素之间的差之和,使之和最小。

示例:

array size even say 8.
array elements : 1,3,4,6,3,4,100,200

Ans:
5

说明:

在这里,我将删除 100 和 200,因为将它们配对可以得到 (200 - 100) = 100 的差异。 所以剩下的元素是 [1,3,4,6,3,4] 最小和的对是:(1 3),(4 3),(6 4)。 = |3-1| = 2, |4-3|=1,|6-4| = 2. 所以总和 = 2 + 1 + 2 = 5

示例:

array size even say 4.
array elements : 1,50,51,60

Ans:
1

说明:这里我将去掉 1 和 60,这样我会得到最小的总和。 所以剩下的元素是 [50, 51],与相邻的 [50 51] = 1 相同。我的代码在这种情况下会失败并返回 49。

如何在java中实现这一点?

我尝试像这样对元素进行排序,但这不是所有类型输入的正确方法。

public static int process(int[] a) {
   int n = a.length;
   int n1 = n/2-1;
   Arrays.sort(arr);
   int sum = 0;
   for(int i=0; i<n1*2; i+=2) {
     sum += a[i+1] - a[i];
   }
   return sum;
}

【问题讨论】:

  • 你能提供这不起作用的输入示例吗?您如何决定移除哪一对?
  • @Scott Hunter,我提供了它们,数组元素:1,50,51,60。对于这个输入,它失败了。我必须找到最小总和作为结果,因此我将删除任何 2 个元素。
  • 您的代码似乎没有尝试删除 2 个元素;如果我错过了,你能指出它在哪里做的吗?
  • @ScottHunter,这部分代码执行int n1 = n/2-1; 然后i&lt;n1*2
  • 对数组进行排序是第一步。接下来,计算相邻数字之间的差异。找到最大的差异并删除数字。重复找出最大的差异并删除数字。最后,计算剩余差值的总和。

标签: java algorithm


【解决方案1】:

在这类问题中,真正的问题是找到一个好的算法。
本帖将坚持这方面。最后提供了一个 C++ 代码来说明它。

很明显,我们必须从对数组进行排序开始。
一个解决方案在于迭代计算三个和,其中

sum0 is the minimum sum assuming no element has been removed
sum1 is the minimum sum assuming one element has been removed
sum2 is the minimum sum assuming two elements has been removed

在此过程中,代码必须跟踪可用于计算差异的最后一个元素, 每个总和一个 (i_dispo0, i_dispo1, i_dispo2)。

原则:

- if sum1 > sum0: sum1 is replaced by sum0
- if sum2 > sum1: sum2 is replaced by sum1

复杂性:O(n logn)用于排序,O(n)用于优化阶段。

代码:

该算法由以下 C++ 中的简单代码说明。
应该很容易理解。

输出: 5 1 2 0 2

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

int min_sum_diff (std::vector<int>& arr) {
    int n = arr.size();
    if (n%2) exit (1);
    std::sort (arr.begin(), arr.end());
    int sum0 = 0, sum1 = arr[n-1] - arr[0] + 1, sum2 = arr[n-1] - arr[0] + 1;
    int i_dispo0 = -1, i_dispo1 = -1, i_dispo2 = -1;
    for (int i = 0; i < n; ++i) {
        int sum0_old = sum0;
        int sum1_old = sum1;
        if (i_dispo0 == -1) {
            i_dispo0 = i;
        } else {
            sum0 += arr[i] - arr[i_dispo0];
            i_dispo0 = -1;
        }
        if (i_dispo1 == -1) {
            i_dispo1 = i;
        } else {
            int add = arr[i] - arr[i_dispo1];
            if (sum0_old < sum1 + add) {
                sum1 = sum0_old;
                i_dispo1 = i;
            } else {
                sum1 += add;
                i_dispo1 = -1;
            }
        }
        if (i_dispo2 == -1) {
            i_dispo2 = i;
        } else {
            sum2 += arr[i] - arr[i_dispo2];
            i_dispo2 = -1;
        }
        
        if (sum2 > sum1_old) {
            sum2 = sum1_old;
            i_dispo2 = i;
        }
        //std::cout << i << " : " << sum0 << "  " << sum1 << "  " << sum2 << '\n';
    }
    return sum2;
}

int main() {
    std::vector<std::vector<int>> examples = {
        {1, 3, 4, 6, 3, 4, 100, 200},   // -> 5
        {1, 50, 51, 60},                // -> 1
        {1,2,100,200,400,401},          // -> 2
        {1, 10, 10, 20, 30, 30},        // -> 0
        {1, 10, 11, 20, 30, 31}         // -> 2
    };
    for (std::vector<int>& arr: examples) {
        int sum = min_sum_diff (arr);
        std::cout << sum << '\n';
    }
    return 0;
}

【讨论】:

  • 有人刚刚对这个答案投了反对票。如果其中有任何错误,我想知道以便能够更正它。
  • 我觉得不错。 耸耸肩
【解决方案2】:

简单的代码块便于理解


Java 代码

package com.company;

import java.util.Arrays;

public class Main {

    public static void main(String[] args) {
    // write your code here
        System.out.println(MinSumDiff(new int[] {1,2,3,4,4,6,100,200}));
    }

    private static int MinSumDiff(int[] arr) {
        Arrays.sort(arr);
        int n = arr.length;
        if(!(0==(n%2))){
            System.exit(2);// '2' refers not divisible by 2
        }
        int sum0 = 0,
            sum1 = arr[n-1] - arr[0] + 1,//LAST ITEM OF ARRAY(LARGEST) - FIRST(SMALLEST) ITEM
            sum2 = sum1;
        int dispo0 = -1,
            dispo1 = -1,
            dispo2 = -1;

        for (int i=0;i<n;i++){
            int sum0_old = sum0;//SET OLD TO CURRENT BECAUSE CURRENT(SUM0) WILL CHANGE LATER, WE CAN USE THIS FOR VERIFICATION
            int sum1_old = sum1;//SET OLD TO CURRENT BECAUSE CURRENT(SUM1) WILL CHANGE LATER, WE CAN USE THIS FOR VERIFICATION
            //------------------------------------------------------------
            if (dispo0 == -1) {
                dispo0 = i;//SET CURENT `I` FOR USE FOR NEXT TIME'S PREVIOUS ...[1]
            } else {
                sum0 += arr[i] - arr[dispo0];//CURRENT ITEM - PREVIOUS ITEM(PREVIOUS FROM [1])
                dispo0 = -1;//REPEAT 
            }
            //------------------------------------------------------------
            if (dispo1 == -1) {
                dispo1 = i;//SET CURENT `I` FOR USE FOR NEXT TIME'S PREVIOUS ...[2]
            } else {
                int add = arr[i] - arr[dispo1];//CURRENT ITEM - PREVIOUS ITEM(PREVIOUS FROM [2])
                if (sum0_old < sum1 + add) {
                    sum1 = sum0_old;//IF `SUM0(OLD)` IS LESS THAN `SUM1` THEN WHY DO YOU AVOID IT,SO SET `SUM1` TO `SUM0(OLD)`
                    dispo1 = i;//SETS `I` NOT AS -1 BECAUSE IT WANTS TO DO THIS AGAIN AND CHECK
                } else {
                    sum1 += add;//IF ADDING THE VARIABLE `ADD` WILL BE LESS THAN SUM0(OLD) THEN YOU CAN ADD IT TO `SUM1` NOW
                    dispo1 = -1;//REPEAT 
                }
            }
            //------------------------------------------------------------
            if (dispo2 == -1) {
                dispo2 = i;//SET CURENT `I` FOR USE FOR NEXT TIME'S PREVIOUS ...[3]
            } else {
                sum2 += arr[i] - arr[dispo2];//CURRENT ITEM - PREVIOUS ITEM(PREVIOUS FROM [3]) ...4
                dispo2 = -1;//REPEAT 
            }
            //------------------------------------------------------------
            if (sum2 > sum1_old) {
                sum2 = sum1_old;//IF `SUM1(OLD)` IS LESS THAN `SUM2` THEN WHY DO YOU AVOID IT,SO SET `SUM2` TO `SUM1(OLD)`
                dispo2 = i;//EVEN THOUGH `DISPO2` IS -1 IT SETS TO `I` BECAUSE OF ABOVE CODE EXECUTION (WHICH IS USEFUL FOR [4])
            }
            //------------------------------------------------------------
        }
        return sum2;//RETURN THE FINAL RESULT THAT'S STORED IN LAST VARIABLE SUM2!!!
    }
}


附言

创意来自@danien'sanswer

我已经用 explained cmets

将其转换为块代码和 java 代码

【讨论】:

    【解决方案3】:

    我针对各种其他输入测试了之前发布的解决方案,但它对不同的输入集无效。因此,我发布了一种适用于不同输入集的不同方法。

    方法:-

    • 对数组进行升序排序

    • 取一个变量将minimum sum初始化为数组的sum of difference of the last two and first two elements

    • 开始一个loop(最初是excluding the last two elements of array)。

    • 如果当前循环运行的总数最小,则每次循环运行应calculate the sum of difference of pairscompare it with minimum sum 更新其值。

    • 因此,在我们完成循环执行后,我们将覆盖所有对,并得到最小总和。

          public static int findMinSum(int[] a){
                  Arrays.sort(a);
                  int minSum=a[1]-a[0]+a[a.length-1]-a[a.length-2];
                  for (int i=0,j=a.length-2;i<a.length/2 && j<a.length;i++,j++){
                      int sum=0;
                      int counter=i;
                      while(counter<j){
                          sum=sum+(a[counter+1]-a[counter]);
                          counter+=2;
                      }
                      if(sum < minSum){
                          minSum=sum;
                      }
                  }
                  return minSum;
              }
      

    例子:-

    - original array={95,98,100,101,102,110}
    - initialize minimum sum to (110-102)+(98-95) = 11
    - loop stages : 
         - (98-95) +(101-100) = 4 (new minimum sum=4)
         - (100-98)+(102-101) = 3 (new minimum sum=3)
         - (101-100)+(110-102) = 9 (minimum sum remains 3)   
    

    我已经进一步测试了以下输入集:-

    {1,3,3,4,4,6,100,200}
    {1,50,51,60}
    {1,2,100,200,400,401}
    {1,2,100,300,400,401}
    {1,2,3,4,4,6,100,200}
    {95,98,100,101,102,401}
    

    如果任何输入集出现任何差异,请告知。

    【讨论】:

    • 好的,由于零,它恰好适用于示例,但是当正确答案是 4 时,它会为 1,2,3,4,4,6,100,200 返回 5
    • -David Eisenstat 感谢您的指出。对代码进行了必要的修改。
    【解决方案4】:

    OP 声明在第一步中可以从数组中删除任何两个元素。如果是这种情况,那么此后的问题就等于在图中找到最大加权匹配。参见例如this解释。

    这个问题的算法实现可以在 Boost (C++) 中找到here(对问题本身有很好的解释)。在 OP 所追求的 Java 中,可以在 JGraphT 库中找到几种算法 here

    请注意,通常算法是针对最大权重编写的,而 OP 是在最小权重之后编写的。以这种形式投射图表的策略是:

    • 创建一个图,其中数组的每个元素都是一个顶点
    • 每条边(每个顶点之间的连接)的权重等于两个顶点值的绝对差乘以 -1

    为了说明,在原始示例中,删除 100 和 200 后,数组中仍保留以下元素:1,3,4,6,3,4。每个元素代表一条边(比如 A、B、C、D、E、F)。顶点 A-B 的权重为 -2,顶点 A-C 的权重为 -3,以此类推。如果我们应用一种算法来找到该图中最大权重的最大匹配,它将对应于 OP 问题中的最小权重。

    【讨论】:

      猜你喜欢
      • 2022-01-23
      • 2021-05-02
      • 1970-01-01
      • 2010-09-23
      • 1970-01-01
      • 2013-03-01
      • 1970-01-01
      • 2016-06-03
      • 1970-01-01
      相关资源
      最近更新 更多