【问题标题】:Minimize the maximum difference between the heights最小化高度之间的最大差异
【发布时间】:2020-11-09 23:33:15
【问题描述】:

给定 n 个塔的高度和 k 值。我们需要将每个塔的高度增加或减少 k(仅一次),其中 k > 0。任务是最小化修改后最长和最短塔的高度之间的差异,并输出此差异。

我得到了解决方案背后的直觉,但我无法评论以下解决方案的正确性。



// C++ program to find the minimum possible 
// difference between maximum and minimum 
// elements when we have to add/subtract 
// every number by k 
#include <bits/stdc++.h> 
using namespace std; 
  
// Modifies the array by subtracting/adding 
// k to every element such that the difference 
// between maximum and minimum is minimized 
int getMinDiff(int arr[], int n, int k) 
{ 
    if (n == 1) 
       return 0; 
  
    // Sort all elements 
    sort(arr, arr+n); 
  
    // Initialize result 
    int ans = arr[n-1] - arr[0]; 
  
    // Handle corner elements 
    int small = arr[0] + k; 
    int big = arr[n-1] - k; 
    if (small > big) 
       swap(small, big); 
  
    // Traverse middle elements 
    for (int i = 1; i < n-1; i ++) 
    { 
        int subtract = arr[i] - k; 
        int add = arr[i] + k; 
  
        // If both subtraction and addition 
        // do not change diff 
        if (subtract >= small || add <= big) 
            continue; 
  
        // Either subtraction causes a smaller 
        // number or addition causes a greater 
        // number. Update small or big using 
        // greedy approach (If big - subtract 
        // causes smaller diff, update small 
        // Else update big) 
        if (big - subtract <= add - small) 
            small = subtract; 
        else
            big = add; 
    } 
  
    return  min(ans, big - small); 
} 
  
// Driver function to test the above function 
int main() 
{ 
    int arr[] = {4, 6}; 
    int n = sizeof(arr)/sizeof(arr[0]); 
    int k = 10; 
    cout << "\nMaximum difference is "
        << getMinDiff(arr, n, k); 
    return 0; 
} 

谁能帮我提供这个问题的正确解决方案?

【问题讨论】:

标签: c++ arrays optimization greedy


【解决方案1】:

上面的代码可以工作,但是我找不到太多解释,所以我会尝试添加一些以帮助培养直觉。

对于任何给定的塔,您有两种选择,您可以增加或减少它的高度。
现在,如果您决定将其高度从 Hi 增加到 Hi + K,那么您也可以增加所有较短塔的高度,因为这不会影响最大值。
同样,如果您决定将塔的高度从 Hi 降低到 Hi - K,那么你也可以降低所有高塔的高度。
我们将利用这一点,我们有 n 座建筑物,我们将尝试使每座建筑物都最高,看看哪座建筑物最高可以给我们最小的高度范围(这是我们的答案)。
让我解释一下:

所以我们要做的是 -
1) 我们首先对数组进行排序(您很快就会明白为什么)。

2)然后对于从 i = 0 到 n-2[1] 的每一栋建筑物,我们尝试使其最高(通过将 K 添加到建筑物,将 K 添加到其左侧的建筑物并减去 K从右边的建筑物)。

假设我们正在构建 Hi,我们已经将 K 添加到它和它之前的建筑物中,并从之后的建筑物中减去 K

所以现在建筑物的最小高度将是 min(H0 +  K, Hi+1 - K),
ie min(1st building + K, next building on right - K)


(注意:这是因为我们对数组进行了排序。举几个例子来说服自己。)

同样,建筑物的最大高度将为 ma​​x(Hi + K, Hn-1 - K),
即最大值(当前建筑 + K,右侧最后一个建筑 - K)


3) max - min 为您提供范围。

[1]注意当 i = n-1 时。在这种情况下,当前建筑物之后没有建筑物,因此我们将 K 添加到每个建筑物,因此范围将仅为 height[n-1] - height[0] 因为 K 被添加到所有内容中,所以它被抵消了。

这是基于上述思想的Java实现:

class Solution {
    int getMinDiff(int[] arr, int n, int k) {
        Arrays.sort(arr);
        int ans = arr[n-1] - arr[0];
        int smallest = arr[0] + k, largest = arr[n-1]-k;
        for(int i = 0; i < n-1; i++){
            int min = Math.min(smallest, arr[i+1]-k);
            int max = Math.max(largest, arr[i]+k);
            if(min < 0) 
                continue;
            ans = Math.min(ans, max-min);
        }
        return ans;
    }
}

【讨论】:

  • 很好的解释?
【解决方案2】:
class Solution:
    def getMinDiff(self, arr, n, k):
        # code here
        arr.sort()
        res = arr[-1]-arr[0]
        
        for i in range(1, n):
            if arr[i]>=k:
                # at a time we can increase or decrease one number only. 
                # Hence assuming we decrease ith elem, we will increase i-1 th elem.
                # using this we basically find which is new_min and new_max possible 
                # and if the difference is smaller than res, we return the same. 
                new_min = min(arr[0]+k, arr[i]-k)
                new_max = max(arr[-1]-k, arr[i-1]+k)
                res = min(res, new_max-new_min)
        
        return res

【讨论】:

  • 请添加更多详细信息以扩展您的答案,例如工作代码或文档引用。
【解决方案3】:
class Solution {
  public:
    int getMinDiff(int arr[], int n, int k) {
            sort(arr, arr+n);
        int diff = arr[n-1]-arr[0];
        int mine, maxe;
        for(int i = 0; i < n; i++)
            arr[i]+=k;
        mine = arr[0];
        maxe = arr[n-1]-2*k;
        for(int i = n-1; i > 0; i--){
            if(arr[i]-2*k < 0)
                break;
            mine = min(mine, arr[i]-2*k);
            maxe =  max(arr[i-1], arr[n-1]-2*k);
            diff = min(diff, maxe-mine);
        }
        return diff;
    }
};

【讨论】:

    【解决方案4】:

    这里是 C++ 代码,我从你离开的地方继续。代码一目了然。

    #include <iostream>
    #include <vector>
    #include <algorithm>
    using namespace std;
    
    int minDiff(int arr[], int n, int k)
    {
        // If the array has only one element.
        if (n == 1)
        {
            return 0;
        }
        //sort all elements
        sort(arr, arr + n);
    
        //initialise result
        int ans = arr[n - 1] - arr[0];
    
        //Handle corner elements
        int small = arr[0] + k;
        int big = arr[n - 1] - k;
        if (small > big)
        {
            // Swap the elements to keep the array sorted.
            int temp = small;
            small = big;
            big = temp;
        }
    
        //traverse middle elements
        for (int i = 0; i < n - 1; i++)
        {
            int subtract = arr[i] - k;
            int add = arr[i] + k;
    
            // If both subtraction and addition do not change the diff.
            // Subtraction does not give new minimum.
            // Addition does not give new maximum.
            if (subtract >= small or add <= big)
            {
                continue;
            }
    
            // Either subtraction causes a smaller number or addition causes a greater number.
            //Update small or big using greedy approach.
            // if big-subtract causes smaller diff, update small Else update big
            if (big - subtract <= add - small)
            {
                small = subtract;
            }
            else
            {
                big = add;
            }
        }
        return min(ans, big - small);
    }
    
    int main(void)
    {
        int arr[] = {1, 5, 15, 10};
        int n = sizeof(arr) / sizeof(arr[0]);
        int k = 3;
        cout << "\nMaximum difference is: " << minDiff(arr, n, k) << endl;
        return 0;
    }
    

    【讨论】:

      【解决方案5】:

      这是一个解决方案:-

      但在开始讨论解决方案之前,这里有一些了解它所需的信息。在最好的情况下,最小差异为零。这只会在两种情况下发生 - (1) 数组包含重复项或 (2) 对于一个元素,比如说“x”,数组中存在另一个元素,其值为“x + 2*k”。

      这个想法很简单。

      1. 首先我们将对数组进行排序
      2. 接下来,我们将尝试找到最佳值(答案将为零)或至少最接近最佳值的数字使用二分搜索

      这是算法的 Javascript 实现:-

      function minDiffTower(arr, k) {
          arr = arr.sort((a,b) => a-b);
          let minDiff = Infinity;
          let prev = null;
      
          for (let i=0; i<arr.length; i++) {
              let el = arr[i];
              
              // Handling case when the array have duplicates
              if (el == prev) {
                  minDiff = 0;
                  break;
              }
              prev = el;
      
              let targetNum = el + 2*k; // Lets say we have an element 10. The difference would be zero when there exists an element with value 10+2*k (this is the 'optimum value' as discussed in the explaination
              let closestMatchDiff =  Infinity; // It's not necessary that there would exist 'targetNum' in the array, so we try to find the closest to this number using Binary Search
              let lb = i+1;
              let ub = arr.length-1;
              while (lb<=ub) {
                  let mid = lb + ((ub-lb)>>1);
                  let currMidDiff =  arr[mid] > targetNum ? arr[mid] - targetNum : targetNum - arr[mid];
                  closestMatchDiff = Math.min(closestMatchDiff, currMidDiff); 
                  if (arr[mid] == targetNum) break; // in this case the answer would be simply zero, no need to proceed further
                  else if (arr[mid] < targetNum) lb = mid+1;
                  else ub = mid-1;
              }
              minDiff = Math.min(minDiff, closestMatchDiff);
          }
          return minDiff;
      }
      

      【讨论】:

        【解决方案6】:

        这个 python 代码可能对你有一些帮助。代码是不言自明的。

        def getMinDiff(arr, n, k):
            arr = sorted(arr)
            ans = arr[-1]-arr[0] #this case occurs when either we subtract k or add k to all elements of the array
            for i in range(n):
                mn=min(arr[0]+k, arr[i]-k) #after sorting, arr[0] is minimum. so adding k pushes it towards maximum. We subtract k from arr[i] to get any other worse (smaller) minimum. worse means increasing the diff b/w mn and mx
                mx=max(arr[n-1]-k, arr[i]+k) # after sorting, arr[n-1] is maximum. so subtracting k pushes it towards minimum. We add k to arr[i] to get any other worse (bigger) maximum. worse means increasing the diff b/w mn and mx
                ans = min(ans, mx-mn)
            return ans
        

        【讨论】:

        • 另外添加 if 语句来检查 arr[i] 是否大于k。我们不能有负高度吗?
        • @rahulsharma 我在代码中添加了更多的 cmets。看看它是否有帮助。尝试干运行代码,你会得到直觉。
        【解决方案7】:
        int getMinDiff(int a[], int n, int k) {
                sort(a,a+n); 
                int i,mx,mn,ans;
                ans = a[n-1]-a[0];  // this can be one possible solution
                
                for(i=0;i<n;i++)
                {
                    if(a[i]>=k)  // since height of tower can't be -ve so taking only +ve heights
                    {
                        mn = min(a[0]+k, a[i]-k);
                        mx = max(a[n-1]-k, a[i-1]+k);
                        ans = min(ans, mx-mn);
                    }
                }
                return ans;
            }
        

        这是 C++ 代码,它通过了所有的测试用例。

        【讨论】:

        • 你能说出它为什么起作用吗?我没有得到这背后的直觉?尽管有问题的代码共享是错误的
        • 索引应该从 1 开始以避免越界异常
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-11-05
        • 1970-01-01
        • 2016-07-23
        • 1970-01-01
        • 2013-04-03
        • 2015-05-04
        相关资源
        最近更新 更多