【问题标题】:Maximum Product Subarray最大乘积子数组
【发布时间】:2014-08-31 10:07:53
【问题描述】:

我有一个由 n 个正实数组成的数组

我必须找出这个给定数组的最大乘积子数组。

该问题如何实现DP解决方案?

详细解释解决方案的DP公式。

【问题讨论】:

  • 到目前为止你做了什么?
  • 看来这是对最大子数组子问题的简单修改,solution for which 可以很容易地适应乘法。或者还有什么其他的吗?

标签: algorithm dynamic-programming


【解决方案1】:

由于最大 sum 的解是已知的,因此您可以

  • 将每个数组项的log 计算到另一个数组中
  • 将已知算法应用于新数组
  • exp的结果就是答案。

(但您可以简单地调整现有算法,这在@nevets 的答案中已经提到。用 1 替换常数 0(这是加性中性元素)。)

【讨论】:

  • 准确率如何?
  • @nevets:当我们谈论浮点乘法时,准确性是一个更大的问题。仅仅摆脱log 不会使计算准确。只有当各个术语的准确度限制已知时,我们才能开始谈论准确度。
  • 如果某些输入数字是负数怎么办?
  • @JeffE:那么这是一个不同的,稍微复杂一点的问题。
【解决方案2】:

它与Maximum Sum of Subarray 问题非常相似,并且比Maximum Product of Subarray which allows negative number 容易得多。核心思路相同:currentMax = max(a[i], some_operation(currentMax, a[i]))

对于每个元素,我们有 2 个选项:将其放入一个连续的子数组中,或者用它开始一个新的子数组。

double currentMax = a[0];

for (int i = 1; i < size; i++)
{
     currentMax = max(a[i], currentMax * a[i]);
     best = max(best, currentMax);
}

【讨论】:

    【解决方案3】:

    我通过 Leetcode OJ 的代码:

    class Solution {
    public:
        int maxProduct(int A[], int n) {
            if (n==0) return 0;
    
            int maxi = 1, mini = 1;
            int out = INT_MIN;
    
            for (int i=0;i<n;i++) {
                int oldmaxi = max(maxi,1);
                if (A[i] > 0) {
                    maxi = oldmaxi*A[i];
                    mini *= A[i];
                } else {
                    maxi = mini*A[i];
                    mini = oldmaxi*A[i];
                }
                out = max(out,maxi);
            }
    
            return out;
        }
    };
    

    可以在这里找到解释: http://changhaz.wordpress.com/2014/09/23/leetcode-maximum-product-subarray/

    【讨论】:

      【解决方案4】:

      我的 Java 解决方案涵盖了输入数组也可能包含负数的情况:

      public class MaximumProductSubarraySolver
      {
          public int maxProduct(int[] a)
          {
              int max_so_far = a[0];
              int max_ending_here = a[0];
              int min_ending_here = a[0];
              for (int i = 1; i < a.length; i++)
              {
                  int max1 = max_ending_here * a[i];
                  int min1 = min_ending_here * a[i];
      
                  max_ending_here = Math.max(a[i], Math.max(min1, max1));
                  min_ending_here = Math.min(a[i], Math.min(min1, max1));
      
                  max_so_far  = Math.max(max_so_far, max_ending_here);
              }
              return max_so_far;
          }
      }
      

      Leetcode 接受。

      更新:在a[i]max1min1 三个数字中查找最小值和最大值的以下(非常简单的)优化带来了巨大的性能提升:

      public class MaximumProductSubarraySolver {
          public int maxProduct(int[] a) {
              int max_so_far, max_ending_here, min_ending_here;
              max_so_far = max_ending_here = min_ending_here = a[0];
      
              for (int i = 1; i < a.length; i++)
              {
                  int max1 = max_ending_here * a[i];
                  int min1 = min_ending_here * a[i];
      
                  // find min and max among a[i], max1, and min1
                  // max_ending_here = max(a[i], max1, min1)
                  // min_ending_here = min(a[i], max1, min1)
                  if(a[i] >= min1)
                  {
                      if(min1 >= max1)
                      {
                          max_ending_here = a[i];
                          min_ending_here = max1;
                      }
                      else
                      {
                          // a[i] >= min1
                          // max1 > min1
                          min_ending_here = min1;
                          max_ending_here = a[i] >= max1 ? a[i] : max1;
                      }
                  }
                  else
                  {
                      // a[i] < min1
                      if(min1 <= max1)
                      {
                          max_ending_here = max1;
                          min_ending_here = a[i];
                      }
                      else
                      {
                          //a[i] < min1
                          //max1 < min1
                          max_ending_here = min1;
                          min_ending_here = a[i] <= max1 ? a[i] : max1;
                      }
                  }
      
                  if(max_ending_here > max_so_far)
                  {
                      max_so_far  = max_ending_here;
                  }
              }
      
              return max_so_far;
          }
      }
      

      优化Leetcode上的代码。

      这让我开始思考是否可以简化这段代码。这是我想出的:

      public class MaximumProductSubarraySolver {
          public int maxProduct(int[] a) {
              int max_so_far, max_ending_here, min_ending_here;
              max_so_far = max_ending_here = min_ending_here = a[0];
      
              for (int i = 1; i < a.length; i++)
              {
                  if(a[i] < 0)
                  {
                      // when a[I] < 0
                      //     max * a[i] will become min
                      //     min * a[i] will become max
                      int t = max_ending_here;
                      max_ending_here = min_ending_here;
                      min_ending_here = t;
                  }
      
                  int max1 = max_ending_here * a[i];
                  int min1 = min_ending_here * a[i];
      
                  max_ending_here = a[i] > max1 ? a[i] : max1;
                  min_ending_here = a[i] < min1 ? a[i] : min1;
      
                  if(max_ending_here > max_so_far)
                  {
                      max_so_far  = max_ending_here;
                  }
              }
      
              return max_so_far;
          }
      }
      

      Leetcode 接受。

      【讨论】:

        【解决方案5】:

        对于每个数组,每次都更新最大值和最小值。 min*A[i] 可能是最大值。 并且代码在这里,在 leetcode 中传递:

        public class Solution {
            public int maxProduct(int[] A) {
                int max = A[0];
                int min = A[0];
                int maxProduct = A[0];
        
                for(int i = 1; i < A.length; i ++) {
                    int temp = max;
                    max = Math.max(Math.max(A[i], max*A[i]), min*A[i]);
                    min = Math.min(Math.min(A[i], min*A[i]), temp*A[i]);
                    if(max > maxProduct)
                        maxProduct = max;
                }
                return maxProduct;
            }
        }
        

        【讨论】:

        • 一个class 运动的int maxProduct(int[] A) 怎么配得上Solution 的名字来解决一个关于n 个正实数数组 的问题?处理min 会增加什么价值? (因为对于大于零的整数,答案是微不足道的,我会用float 戳:)它适用于{ 0.1, 9 }吗?如果是这样,如何从最大乘积到对应的子数组?
        【解决方案6】:

        这里是 Ruby 的一个实现:

        def max_subarray_product(arr)
          maxi = 1
          mini = 1
          result = 0
        
          arr.each do |i|
            temp_max =  maxi > 1 ? maxi : 1
            if (i > 0)
              maxi = temp_max*i
              mini *= i
            else
              maxi = mini*i
              mini = temp_max*i
            end
            result = maxi > result ? maxi : result
          end
          result
        end
        

        例如:

        a = [6, -3, -10, 0, 2]
        puts maxsubarrayproduct(a)
        

        输出:

        180
        

        【讨论】:

          【解决方案7】:

          示例说明:

          输入 = [ 2, 3, -2, 4 ]

          product_left_to_right = 输入 = [ 2, 3, -2, 4 ]

          product_right_to_left = 输入[::-1] = [ 4, -2, 3, 2 ]

          第一次迭代:

          6 = 3 * 2 product_left_to_right = [ 2, 6, -2, 4 ]

          -8 = -2 * 4 product_right_to_left = [ 4, -8, 3, 2 ]

          第二次迭代:

          -12 = -2 * 6 product_left_to_right = [ 2, 6, -12, 4 ]

          -24 = 3 * -8 product_right_to_left = [ 4, -8, -24, 2 ]

          第三次迭代:

          -48 = 4 * -12 product_left_to_right = [ 2, 6, -12, -48 ]

          -48 = 2 * -24 product_right_to_left = [ 4, -8, -24, -48 ]

          最大值比较:

          product_left_to_right 的最大值 = [ 2, 6, -12, -48 ] = 6

          product_right_to_left 的最大值 = [ 4, -8, -24, -48 ] = 4

          最大 (6, 4) = 6

          返回 6

          def maxProduct(self, nums: List[int]) -> int:
                  l = len(nums)
                  nums_l=nums //product_left_to_right 
                  nums_r = nums[::-1] //product_right_to_left
                  for i in range(1,l,1):
                      nums_l[i] *= (nums_l[i-1] or 1) //if meets 0 then restart in-place by itself.
                      nums_r[i] *= (nums_r[i-1] or 1) 
                  return max(max(nums_l), max(nums_r))
          

          【讨论】:

            【解决方案8】:

            在 java 中非常适合我。所有测试用例都通过了。运行时间 1 毫秒。

            public int maxProduct(int[] nums) {
            
                int curr_max_prod= nums[0];
            
                int curr_min_prod= nums[0];
            
                int prev_max= nums[0];
            
                int prev_min = nums[0];
                int ans= nums[0];
            
                for(int i=1;i<nums.length;i++){
                    int k= Math.max(nums[i]*prev_max, nums[i]*prev_min);
                    curr_max_prod=Math.max(k, nums[i]);
            
                    int h =Math.min(nums[i]*prev_max, nums[i]*prev_min);
            
                    curr_min_prod= Math.min(h, nums[i]);
            
                    ans=Math.max(ans,curr_max_prod);
                    prev_max=curr_max_prod;
                    prev_min=curr_min_prod;
            
            
                }
                return ans;
            
            
            }
            

            【讨论】:

              【解决方案9】:

              由于数字都是正数,因此将它们全部相乘。

              【讨论】:

              • 提到它们是实数。例如:{0.5 , 2, 3, 4} 自己寻找
              猜你喜欢
              • 2020-08-30
              • 2021-08-21
              • 2020-09-18
              • 1970-01-01
              • 2023-03-17
              • 1970-01-01
              • 2014-07-14
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多