如何找到产品大于总和的货币对

时间:2014-09-09 17:03:26

标签: java algorithm time-complexity

假设我们有一个数组C,其中包含C中的所有元素> 0

如果(a, b)0 ≤ a < b < N,则一对索引C[a] * C[b] ≥ C[a] + C[b]可以成倍增加。 时间复杂度是O(n)

  • 预期最坏情况时间复杂度为O(N);

感谢您帮助支持此案例。 感谢。

8 个答案:

答案 0 :(得分:9)

O(N)解决方案就在这里。

假设数组中的元素按非递减顺序排序。 如果数组未排序,那么最差时间复杂度O(N)是不可解密的。

原始条件C[a] * C[b] ≥ C[a] + C[b]可以很容易地表达为C[b] ≥ C[a] / (C[a] - 1)

所以,看看C[a] / (C[a] - 1) ...

的图表

enter image description here

......我们可以看到:

  1. 如果0 ≤ C[a] < 1,那么(a, b)只有在C[a] = 0
  2. 时才会相乘
  3. 如果1 < C[a] < 2,则为C[a] / (C[a] - 1) > 2,因此C[b] > 2。所以,C[b] > C[a],但这是不可能的,因为C[a] ≥ C[b](因为数组已排序)。
  4. 如果C[a] > 2,则该对可以乘以C[b]
  5. 的任何C[b] ≥ C[a] / (C[a] - 1)

    因此,C#中的代码可能如下所示:

    int count_pairs(double[] C)
    {
      int result = 0;
      int len = C.Length;
    
      if (len > 1)
      {
        int lo_index;
        int hi_index = len - 1;
    
        // Skip all C[i] less than 1
        for (lo_index = 0; lo_index < len; lo_index++)
        {
          if (C[lo_index] > 1)
            break;
        }
    
        while (hi_index > lo_index)
        {
          double v = C[hi_index] / (C[hi_index] - 1);
    
          while (lo_index < hi_index && C[lo_index] < v)
          {
            lo_index++;
          }
    
          if (lo_index == hi_index)
            break;
    
          result += (hi_index - lo_index);
    
          hi_index--;
    
        }
      }
      return result;  
    }
    

答案 1 :(得分:3)

与竞争问题一样,您必须在编码之前解决问题。在这种情况下,你需要一些基本的代数。我会忽略奇怪的数字格式,只能在C上运行。

因此,给定非负数a,非负数b会产生a * b >= a + b吗? a * b >= a + b =&gt; b * (a-1) >= a
现在,我们有4个案例:

  1. 0&lt; a&lt; 1.在这种情况下,下一步是b <= a / (a-1)。请注意,RHS为负数,而b为非负数。因此没有这样的b
  2. a == 0.下一步是-b >= 0,因此,因为b是非负数, b == 0
  3. a == 1.下一步是b * 0 >= 1,简化为 false 。因此,没有此类b
  4. 1&lt;一个。下一步是b >= a / (a-1)。这是唯一的非平凡案例。
  5. 这本身就已经给你一个O(N * log(N))算法:
    通过数组迭代保持总和。对于每个数字,如果为0,则在数组中找到0的数量并将它们添加到总和中。如果它是0 < num&lt; = 1,添加0.如果是&gt; 1,添加值的数量&gt; = num / (num-1)由于数组是升序的,您可以使用二进制搜索在log(N)时间中查找这些值,从而为您提供总N * log(N)最差运行时(和O(N)最佳运行时,如果所有值都是noops - 介于0和1之间

    为了使算法进一步优化到O(N)的最后一步,您需要观察函数x / (x-1)在x&gt;时的行为。 1和x正在增长(即,当您遍历数组时,搜索目标是什么)。

答案 2 :(得分:2)

我正在做同样的挑战。看起来您忘记了多个输入为0的情况,因为0 * 0 = 0 + 0;

class Solution {
    public static final long ONE_MILLION = 1000000;
    public static final int LIMIT = 1000000000;

    public int solution(int[] A, int[] B) {
        // need to redo algebra term is such that B >= A/A-1
        //so if A < 1 only possible if C[a] =0 and C[b] = 0
        //if A < 2 is impossible since sorted
        //if A > possible

        if (A.length < 2) { return 0;}
        int numberOfMP = 0;
        int lowIndex = 0;
        int highIndex = A.length-1;

        if (A[0] == 0 && B [0] == 0) {
            int index = 1;
            int increaseAmount = 1;
            while (A[index] == 0 && B[index] == 0) {
                numberOfMP += increaseAmount;
                increaseAmount++;
            }

        }

        while(lowIndex < highIndex){
            //don't convert to double to avoid precision errors
            long C_low = convertValue(A[lowIndex], B[lowIndex]);
            long C_high;
            //avoid useless calculations for values we already know will fail
            if(C_low >= ONE_MILLION) {
                C_low = convertValue(A[lowIndex], B[lowIndex]);
                C_high = convertValue(A[highIndex], B[highIndex]);
                //note that since both inputs to the product were scaled, we need to scale the sum twice
                //not a scaleable solution...
                if( C_low * C_high >= (C_low + C_high) * ONE_MILLION) {
                    numberOfMP += highIndex - lowIndex;
                    highIndex--;
                }else{
                    lowIndex++;
                }
            }else{
                lowIndex++;
            }
        }

        if (numberOfMP > LIMIT) {
            return LIMIT;
        }
        return numberOfMP;
    }

    private long convertValue(int a, int b) {
        return (long) a * ONE_MILLION + (long) b;
    }
}

答案 3 :(得分:1)

这是我测试的O#N解决方案的C#版本。

注意:我的练习有点复杂。

传入参数包含N (0..100,000)个非负数,其中A数组包含数字(0..1,000)的整数部分,B数组包含小数数字(0..999,999)的一部分。

AB数组表示的数字按非递减顺序排序。

如果对的数量大于1,000,000,000,则该函数应返回1,000,000,000

class Solution
{
    public int solution(int[] A, int[] B)
    {
        // a == 0    => b == 0
        // 0 < a < 1 => no b
        // a == 1    => no b
        // 1 < a     => b >= a / (a - 1)

        if (A.Length < 2)
        {
            return 0;
        }

        int result = 0;
        int currentIndex = 0;
        int maxIndex = A.Length - 1;

        const int OneMillion = 1000000;
        const int OneBillion = 1000000000;

        // Counting zeros
        while (currentIndex < A.Length && A[currentIndex] == 0 && B[currentIndex] == 0)
        {
            currentIndex++;
        }

        // Adding number of pairs of zeros
        if (currentIndex > 1)
        {
            // n = currentIndex - 1
            // sum(1..n) => (n + 1) * (int)(n / 2) + (n % 2) * (int)((n + 1) / 2)

            decimal n = new Decimal(currentIndex - 1);
            decimal numberOfPairsOfZeros = (n + 1) * (int)(n / 2) + (n % 2) * (int)((n + 1) / 2);

            result += (numberOfPairsOfZeros > OneBillion ? OneBillion : (int)numberOfPairsOfZeros);

            if (result >= OneBillion)
            {
                return OneBillion;
            }
        }

        if (currentIndex == A.Length)
        {
            return result;
        }

        // Skip values where 0 < A.B <= 1.001000
        // 1.001000 can be in pair with numbers >= 1001.000000 but the maximum number is 1000.999999;
        // 1.001001 can be in pair with numbers >= 1000.000999;
        while
        (
            currentIndex < A.Length
            &&
            (
                A[currentIndex] == 0
                ||
                (
                    A[currentIndex] == 1
                    && B[currentIndex] < 1001
                )
            )
        )
        {
            currentIndex++;
        }

        if (currentIndex == A.Length)
        {
            return result;
        }

        // From this point 1 < A.B, so we look for A2.B2 values where A2.B2 >= A.B / (A.B - 1)
        for (int i = currentIndex; i < A.Length - 1; i++)
        {
            // number between 1.001001 and 2 should be searched only
            if (A[i] == 1)
            {
                // Search numbers greater than or equals to A.B / (A.B - 1)
                // A.B / (A.B - 1) = 1.B / (1.B - 1) = 1.B / 0.B = 1 / 0.B + 1
                double scaledOnePerBFractionalPart = (double)OneMillion / (double)B[i] * (double)OneMillion;
                int scaledMinValue = (int)scaledOnePerBFractionalPart + OneMillion + (Math.Ceiling(scaledOnePerBFractionalPart) == scaledOnePerBFractionalPart ? 0 : 1);

                while (maxIndex > i && OneMillion * A[maxIndex] + B[maxIndex] >= scaledMinValue)
                {
                    maxIndex--;
                }

                result += A.Length - 1 - maxIndex;
            }
            else
            {
                // In this case the number is greater than or equal to 2.
                // So we just add the remaining indexes as number of pairs.
                result += A.Length - 1 - i;
            }

            if (result >= OneBillion)
            {
                return OneBillion;
            }
        }

        return result >= OneBillion ? OneBillion : result;
    }
}

答案 4 :(得分:0)

以下条件:

  

如果0≤P<1,则一对指数(P,Q)是乘法的。 Q&lt; N和C [P] * C [Q]≥C[P] + C [Q]。

结合预先排序的数组:

  
      
  1. 从数组创建的实数按非递减顺序排序。
  2.   

允许O(n)解决方案。也许如果您通过解决方案调试,您将看到一种模式。

答案 5 :(得分:0)

针对示例质询的Java解决方案,可读性略高:

recover

答案 6 :(得分:-1)

假设数组中的元素按非递减顺序排序 我在JavaScript中的O(N)解决方案:

function solution(A, B) {
    var scale = 1000000;
    var N = A.length;
    var count=0;
    var left = 0;
    var right = N-1;
    var C_left = 0;

    while(left<right){
    //to avoid rounding errors, use multiplication
        C_left = A[left]*scale+B[left];
        if(C_left>=scale){

            if(C_left*(A[right]*scale+B[right])>=(C_left+(A[right]*scale+B[right]))*scale){
                // if C[left],C[right] is ok, that means that all elements C[left++],C[right] to satisfy condition
                count+=right-left;
                right--;
            }else{
                left++;
            }
        }else{
            left++;
        }
    }
    //condition from codility.com test
    if(count>1000000000)
        return 1000000000;
    else{
        return count;
    }
}

答案 7 :(得分:-1)

我不是在这里复制我的算法,但我的想法似乎也是O(n)。

1,首先计算最小可能的完整图。 将排序后的数组除以2(在中间索引处) - 向上舍入(所以它是左倾树)

并检查第一个元素和最后一个元素,增加第一个索引直到没有通过 - 计算完整图形的边缘 - n *(n-1)/ 2

如果我们没有通过它是O(N)最坏的情况。并且我们不依赖于N存储任何东西,只需2-3个临时int来计算差异(CP * CQ) - (CP + CQ)应该>&gt; = 0

2,删除所有未通过的边缘

所以我们在1)中得到了一个传球,继续检查,但是下一个元素,而不是最后一个,直到我们得到一个传球。每次迭代都会减少第一个结果 - 从完整图中删除,然后返回结果。

2)继续算法,这意味着O(N)最坏情况已经分布在1)和2)之间

不幸的是我只是测试了自己,但不会通过这个测试,因为它至少花了我3个小时。:(