Lợi nhuận bán một lần tối đa


123

Giả sử chúng ta được cung cấp một mảng n số nguyên đại diện cho giá cổ phiếu vào một ngày duy nhất. Chúng tôi muốn tìm một cặp (buyDay, sellDay) , với buyDay ≤ sellDay , sao cho nếu chúng tôi mua cổ phiếu trên buyDay và bán nó trên sellDay , chúng tôi sẽ tối đa hóa lợi nhuận của mình.

Rõ ràng có một giải pháp O (n 2 ) cho thuật toán bằng cách thử tất cả các cặp có thể có (buyDay, sellDay) và tận dụng tốt nhất trong số chúng. Tuy nhiên, có một thuật toán tốt hơn, có lẽ là một thuật toán chạy trong thời gian O (n) ?


2
Đây là bài toán con tổng lớn nhất với mức không hướng.
MSN

2
@MSN: Làm sao vậy? Anh ấy không nhìn vào tổng, mà là sự khác biệt giữa các yếu tố.
PengOne

@ PengOne- Đúng, nhưng câu hỏi đó đã bị đóng lại. Tôi đã sửa lại câu hỏi để dễ hiểu hơn, vì vậy chúng ta có thể cố gắng giữ cho câu hỏi này mở không?
templatetypedef

2
@PengOne, Giống như tôi đã nói, nó có một cấp độ chuyển hướng. Cụ thể, bạn muốn tối đa hóa tổng lãi / lỗ trong một nhóm ngày liền kề. Do đó, hãy chuyển đổi danh sách thành lãi / lỗ và tìm tổng số con tối đa.
MSN

1
@PDN: Điều đó sẽ không hoạt động vì min có thể xảy ra trước max. Bạn không thể bán cổ phiếu (trong trường hợp này) và mua nó sau.
Ajeet Ganga

Câu trả lời:


287

Tôi thích vấn đề này. Đó là một câu hỏi phỏng vấn cổ điển và tùy thuộc vào cách bạn nghĩ về nó, bạn sẽ có được những giải pháp ngày càng tốt hơn. Chắc chắn có thể làm điều này tốt hơn O (n 2 thời gian ) lần, và tôi đã liệt kê ba cách khác nhau để bạn có thể suy nghĩ về vấn đề ở đây. Hy vọng rằng điều này trả lời câu hỏi của bạn!

Đầu tiên, giải pháp chia để trị. Hãy xem liệu chúng ta có thể giải quyết vấn đề này bằng cách chia đầu vào làm đôi, giải quyết vấn đề trong mỗi mảng con, sau đó kết hợp cả hai lại với nhau. Hóa ra chúng tôi thực sự có thể làm điều này và có thể làm điều đó một cách hiệu quả! Theo trực giác thì là thế này. Nếu chúng ta có một ngày duy nhất, lựa chọn tốt nhất là mua vào ngày đó và sau đó bán lại ngay trong ngày mà không có lợi nhuận. Nếu không, hãy chia mảng thành hai nửa. Nếu chúng ta nghĩ về câu trả lời tối ưu có thể là gì, thì nó phải ở một trong ba vị trí:

  1. Cặp mua / bán chính xác hoàn toàn xảy ra trong hiệp một.
  2. Cặp mua / bán chính xác xảy ra hoàn toàn trong hiệp hai.
  3. Cặp mua / bán chính xác xảy ra trên cả hai nửa - chúng tôi mua trong nửa đầu, sau đó bán trong nửa sau.

Chúng ta có thể nhận các giá trị cho (1) và (2) bằng cách gọi đệ quy thuật toán của chúng ta trên nửa thứ nhất và nửa thứ hai. Đối với tùy chọn (3), cách để kiếm lợi nhuận cao nhất là mua ở điểm thấp nhất trong nửa đầu và bán ở điểm lớn nhất trong nửa sau. Chúng ta có thể tìm các giá trị nhỏ nhất và lớn nhất trong hai nửa bằng cách thực hiện quét tuyến tính đơn giản trên đầu vào và tìm hai giá trị. Sau đó, điều này cung cấp cho chúng tôi một thuật toán với sự lặp lại sau:

T(1) <= O(1)
T(n) <= 2T(n / 2) + O(n)

Sử dụng Định lý Master để giải quyết việc lặp lại, chúng ta thấy rằng điều này chạy trong thời gian O (n lg n) và sẽ sử dụng không gian O (lg n) cho các lệnh gọi đệ quy. Chúng tôi vừa đánh bại giải pháp O (n 2 ) ngây thơ !

Nhưng đợi đã! Chúng tôi có thể làm tốt hơn thế này nhiều. Lưu ý rằng lý do duy nhất chúng ta có một số hạng O (n) trong lần lặp lại của chúng ta là chúng ta phải quét toàn bộ đầu vào để cố gắng tìm các giá trị tối thiểu và lớn nhất trong mỗi nửa. Vì chúng ta đã khám phá đệ quy từng nửa, có lẽ chúng ta có thể làm tốt hơn bằng cách đệ quy cũng trả lại các giá trị tối thiểu và tối đa được lưu trữ trong mỗi nửa! Nói cách khác, đệ quy của chúng ta trả lại ba thứ:

  1. Thời gian mua và bán để tối đa hóa lợi nhuận.
  2. Tổng giá trị nhỏ nhất trong phạm vi.
  3. Tổng giá trị lớn nhất trong phạm vi.

Hai giá trị cuối cùng này có thể được tính toán đệ quy bằng cách sử dụng một đệ quy đơn giản mà chúng ta có thể chạy cùng lúc với đệ quy để tính toán (1):

  1. Giá trị tối đa và giá trị nhỏ nhất của một phạm vi phần tử đơn chỉ là phần tử đó.
  2. Giá trị tối đa và giá trị nhỏ nhất của một phạm vi nhiều phần tử có thể được tìm thấy bằng cách tách đầu vào làm đôi, tìm giá trị tối đa và giá trị nhỏ nhất của mỗi nửa, sau đó lấy giá trị lớn nhất và tối thiểu tương ứng của chúng.

Nếu chúng ta sử dụng phương pháp này, mối quan hệ lặp lại của chúng ta bây giờ là

T(1) <= O(1)
T(n) <= 2T(n / 2) + O(1)

Sử dụng Định lý Master ở đây cho chúng ta thời gian chạy của O (n) với không gian O (lg n), thậm chí còn tốt hơn lời giải ban đầu của chúng ta!

Nhưng hãy đợi một chút - chúng tôi có thể làm tốt hơn thế này! Hãy suy nghĩ về việc giải quyết vấn đề này bằng cách sử dụng lập trình động. Ý tưởng sẽ là suy nghĩ về vấn đề như sau. Giả sử rằng chúng ta đã biết câu trả lời cho vấn đề sau khi xem xét k phần tử đầu tiên. Chúng ta có thể sử dụng kiến ​​thức của chúng ta về phần tử (k + 1) st, kết hợp với giải pháp ban đầu của chúng tôi, để giải quyết vấn đề cho các phần tử (k + 1) đầu tiên không? Nếu vậy, chúng ta có thể có được một thuật toán tuyệt vời bằng cách giải bài toán cho phần tử đầu tiên, sau đó là hai phần tử đầu tiên, rồi ba phần tử đầu tiên, v.v. cho đến khi chúng tôi tính toán nó cho n phần tử đầu tiên.

Hãy suy nghĩ về cách làm điều này. Nếu chúng ta chỉ có một yếu tố, chúng ta đã biết rằng nó phải là cặp mua / bán tốt nhất. Bây giờ, giả sử chúng ta biết câu trả lời tốt nhất cho k phần tử đầu tiên và nhìn vào phần tử thứ (k + 1). Sau đó, cách duy nhất để giá trị này có thể tạo ra một giải pháp tốt hơn những gì chúng ta có cho k phần tử đầu tiên là nếu sự khác biệt giữa phần tử nhỏ nhất trong k phần tử đầu tiên và phần tử mới đó lớn hơn sự khác biệt lớn nhất mà chúng tôi đã tính toán cho đến nay. Vì vậy, giả sử rằng khi chúng ta xem xét các phần tử, chúng tôi theo dõi hai giá trị - giá trị tối thiểu mà chúng tôi đã thấy cho đến nay và lợi nhuận tối đa mà chúng tôi có thể kiếm được chỉ với k phần tử đầu tiên. Ban đầu, giá trị tối thiểu mà chúng tôi thấy cho đến nay là yếu tố đầu tiên và lợi nhuận tối đa bằng không. Khi chúng tôi thấy một phần tử mới, trước tiên chúng tôi cập nhật lợi nhuận tối ưu của mình bằng cách tính toán số tiền chúng tôi sẽ kiếm được bằng cách mua ở mức giá thấp nhất cho đến nay và bán ở mức giá hiện tại. Nếu giá trị này tốt hơn giá trị tối ưu mà chúng tôi đã tính toán cho đến nay, thì chúng tôi cập nhật giải pháp tối ưu để trở thành lợi nhuận mới này. Tiếp theo, chúng tôi cập nhật phần tử tối thiểu được thấy cho đến nay thành phần tử nhỏ nhất hiện tại và phần tử mới.

Vì ở mỗi bước, chúng ta chỉ thực hiện công việc của O (1) và chúng ta đang truy cập từng phần tử trong số n phần tử chính xác một lần, điều này cần O (n) thời gian để hoàn thành! Hơn nữa, nó chỉ sử dụng bộ lưu trữ phụ O (1). Điều này tốt như chúng tôi đã nhận được cho đến nay!

Ví dụ: trên đầu vào của bạn, đây là cách thuật toán này có thể chạy. Các số ở giữa mỗi giá trị của mảng tương ứng với các giá trị được giữ bởi thuật toán tại thời điểm đó. Bạn sẽ không thực sự lưu trữ tất cả những thứ này (sẽ chiếm O (n) bộ nhớ!), Nhưng sẽ hữu ích khi thấy thuật toán phát triển:

            5        10        4          6         7
min         5         5        4          4         4    
best      (5,5)     (5,10)   (5,10)     (5,10)    (5,10)

Trả lời: (5, 10)

            5        10        4          6        12
min         5         5        4          4         4    
best      (5,5)     (5,10)   (5,10)     (5,10)    (4,12)

Trả lời: (4, 12)

            1       2       3      4      5
min         1       1       1      1      1
best      (1,1)   (1,2)   (1,3)  (1,4)  (1,5)

Trả lời: (1, 5)

Chúng ta có thể làm tốt hơn bây giờ không? Thật không may, không phải theo nghĩa tiệm cận. Nếu chúng ta sử dụng ít hơn O (n) thời gian, chúng ta không thể xem xét tất cả các con số trên các đầu vào lớn và do đó không thể đảm bảo rằng chúng ta sẽ không bỏ lỡ câu trả lời tối ưu (chúng ta chỉ có thể "ẩn" nó trong các phần tử mà chúng ta không nhìn vào). Thêm vào đó, chúng ta không thể sử dụng bất kỳ không gian nào nhỏ hơn O (1). Có thể có một số tối ưu hóa cho các yếu tố không đổi ẩn trong ký hiệu big-O, nhưng nếu không, chúng tôi không thể mong đợi tìm thấy bất kỳ tùy chọn nào tốt hơn hoàn toàn.

Nhìn chung, điều này có nghĩa là chúng tôi có các thuật toán sau:

  • Ngây thơ: O (n 2 ) thời gian, O (1) không gian.
  • Chia và Chinh phục: O (n lg n) thời gian, O (lg n) không gian.
  • Chia và Chinh phục được tối ưu hóa: O (n) thời gian, O (lg n) không gian.
  • Lập trình động: O (n) thời gian, O (1) không gian.

Hi vọng điêu nay co ich!

CHỈNH SỬA : Nếu bạn quan tâm, tôi đã mã hóa phiên bản Python của bốn thuật toán này để bạn có thể chơi với chúng và đánh giá hiệu suất tương đối của chúng. Đây là mã:

# Four different algorithms for solving the maximum single-sell profit problem,
# each of which have different time and space complexity.  This is one of my
# all-time favorite algorithms questions, since there are so many different
# answers that you can arrive at by thinking about the problem in slightly
# different ways.
#
# The maximum single-sell profit problem is defined as follows.  You are given
# an array of stock prices representing the value of some stock over time.
# Assuming that you are allowed to buy the stock exactly once and sell the
# stock exactly once, what is the maximum profit you can make?  For example,
# given the prices
#
#                        2, 7, 1, 8, 2, 8, 4, 5, 9, 0, 4, 5
#
# The maximum profit you can make is 8, by buying when the stock price is 1 and
# selling when the stock price is 9.  Note that while the greatest difference
# in the array is 9 (by subtracting 9 - 0), we cannot actually make a profit of
# 9 here because the stock price of 0 comes after the stock price of 9 (though
# if we wanted to lose a lot of money, buying high and selling low would be a
# great idea!)
#
# In the event that there's no profit to be made at all, we can always buy and
# sell on the same date.  For example, given these prices (which might
# represent a buggy-whip manufacturer:)
#
#                            9, 8, 7, 6, 5, 4, 3, 2, 1, 0
#
# The best profit we can make is 0 by buying and selling on the same day.
#
# Let's begin by writing the simplest and easiest algorithm we know of that
# can solve this problem - brute force.  We will just consider all O(n^2) pairs
# of values, and then pick the one with the highest net profit.  There are
# exactly n + (n - 1) + (n - 2) + ... + 1 = n(n + 1)/2 different pairs to pick
# from, so this algorithm will grow quadratically in the worst-case.  However,
# it uses only O(1) memory, which is a somewhat attractive feature.  Plus, if
# our first intuition for the problem gives a quadratic solution, we can be
# satisfied that if we don't come up with anything else, we can always have a
# polynomial-time solution.

def BruteForceSingleSellProfit(arr):
    # Store the best possible profit we can make; initially this is 0.
    bestProfit = 0;

    # Iterate across all pairs and find the best out of all of them.  As a
    # minor optimization, we don't consider any pair consisting of a single
    # element twice, since we already know that we get profit 0 from this.
    for i in range(0, len(arr)):
        for j in range (i + 1, len(arr)):
            bestProfit = max(bestProfit, arr[j] - arr[i])

    return bestProfit

# This solution is extremely inelegant, and it seems like there just *has* to
# be a better solution.  In fact, there are many better solutions, and we'll
# see three of them.
#
# The first insight comes if we try to solve this problem by using a divide-
# and-conquer strategy.  Let's consider what happens if we split the array into
# two (roughly equal) halves.  If we do so, then there are three possible
# options about where the best buy and sell times are:
#
# 1. We should buy and sell purely in the left half of the array.
# 2. We should buy and sell purely in the right half of the array.
# 3. We should buy in the left half of the array and sell in the right half of
#    the array.
#
# (Note that we don't need to consider selling in the left half of the array
# and buying in the right half of the array, since the buy time must always
# come before the sell time)
#
# If we want to solve this problem recursively, then we can get values for (1)
# and (2) by recursively invoking the algorithm on the left and right
# subarrays.  But what about (3)?  Well, if we want to maximize our profit, we
# should be buying at the lowest possible cost in the left half of the array
# and selling at the highest possible cost in the right half of the array.
# This gives a very elegant algorithm for solving this problem:
#
#    If the array has size 0 or size 1, the maximum profit is 0.
#    Otherwise:
#       Split the array in half.
#       Compute the maximum single-sell profit in the left array, call it L.
#       Compute the maximum single-sell profit in the right array, call it R.
#       Find the minimum of the first half of the array, call it Min
#       Find the maximum of the second half of the array, call it Max
#       Return the maximum of L, R, and Max - Min.
#
# Let's consider the time and space complexity of this algorithm.  Our base
# case takes O(1) time, and in our recursive step we make two recursive calls,
# one on each half of the array, and then does O(n) work to scan the array
# elements to find the minimum and maximum values.  This gives the recurrence
#
#    T(1)     = O(1)
#    T(n / 2) = 2T(n / 2) + O(n)
#
# Using the Master Theorem, this recurrence solves to O(n log n), which is
# asymptotically faster than our original approach!  However, we do pay a
# (slight) cost in memory usage.  Because we need to maintain space for all of
# the stack frames we use.  Since on each recursive call we cut the array size
# in half, the maximum number of recursive calls we can make is O(log n), so
# this algorithm uses O(n log n) time and O(log n) memory.

def DivideAndConquerSingleSellProfit(arr):
    # Base case: If the array has zero or one elements in it, the maximum
    # profit is 0.
    if len(arr) <= 1:
        return 0;

    # Cut the array into two roughly equal pieces.
    left  = arr[ : len(arr) / 2]
    right = arr[len(arr) / 2 : ]

    # Find the values for buying and selling purely in the left or purely in
    # the right.
    leftBest  = DivideAndConquerSingleSellProfit(left)
    rightBest = DivideAndConquerSingleSellProfit(right)

    # Compute the best profit for buying in the left and selling in the right.
    crossBest = max(right) - min(left)

    # Return the best of the three
    return max(leftBest, rightBest, crossBest)

# While the above algorithm for computing the maximum single-sell profit is
# better timewise than what we started with (O(n log n) versus O(n^2)), we can
# still improve the time performance.  In particular, recall our recurrence
# relation:
#
#    T(1) = O(1)
#    T(n) = 2T(n / 2) + O(n)
#
# Here, the O(n) term in the T(n) case comes from the work being done to find
# the maximum and minimum values in the right and left halves of the array,
# respectively.  If we could find these values faster than what we're doing
# right now, we could potentially decrease the function's runtime.
#
# The key observation here is that we can compute the minimum and maximum
# values of an array using a divide-and-conquer approach.  Specifically:
#
#    If the array has just one element, it is the minimum and maximum value.
#    Otherwise:
#       Split the array in half.
#       Find the minimum and maximum values from the left and right halves.
#       Return the minimum and maximum of these two values.
#
# Notice that our base case does only O(1) work, and our recursive case manages
# to do only O(1) work in addition to the recursive calls.  This gives us the
# recurrence relation
#
#    T(1) = O(1)
#    T(n) = 2T(n / 2) + O(1)
#
# Using the Master Theorem, this solves to O(n).
#
# How can we make use of this result?  Well, in our current divide-and-conquer
# solution, we split the array in half anyway to find the maximum profit we
# could make in the left and right subarrays.  Could we have those recursive
# calls also hand back the maximum and minimum values of the respective arrays?
# If so, we could rewrite our solution as follows:
#
#    If the array has size 1, the maximum profit is zero and the maximum and
#       minimum values are the single array element.
#    Otherwise:
#       Split the array in half.
#       Compute the maximum single-sell profit in the left array, call it L.
#       Compute the maximum single-sell profit in the right array, call it R.
#       Let Min be the minimum value in the left array, which we got from our
#           first recursive call.
#       Let Max be the maximum value in the right array, which we got from our
#           second recursive call.
#       Return the maximum of L, R, and Max - Min for the maximum single-sell
#           profit, and the appropriate maximum and minimum values found from
#           the recursive calls.
#
# The correctness proof for this algorithm works just as it did before, but now
# we never actually do a scan of the array at each step.  In fact, we do only
# O(1) work at each level.  This gives a new recurrence
#
#     T(1) = O(1)
#     T(n) = 2T(n / 2) + O(1)
#
# Which solves to O(n).  We're now using O(n) time and O(log n) memory, which
# is asymptotically faster than before!
#
# The code for this is given below:

def OptimizedDivideAndConquerSingleSellProfit(arr):
    # If the array is empty, the maximum profit is zero.
    if len(arr) == 0:
        return 0

    # This recursive helper function implements the above recurrence.  It
    # returns a triple of (max profit, min array value, max array value).  For
    # efficiency reasons, we always reuse the array and specify the bounds as
    # [lhs, rhs]
    def Recursion(arr, lhs, rhs):
        # If the array has just one element, we return that the profit is zero
        # but the minimum and maximum values are just that array value.
        if lhs == rhs:
            return (0, arr[lhs], arr[rhs])

        # Recursively compute the values for the first and latter half of the
        # array.  To do this, we need to split the array in half.  The line
        # below accomplishes this in a way that, if ported to other languages,
        # cannot result in an integer overflow.
        mid = lhs + (rhs - lhs) / 2

        # Perform the recursion.
        ( leftProfit,  leftMin,  leftMax) = Recursion(arr, lhs, mid)
        (rightProfit, rightMin, rightMax) = Recursion(arr, mid + 1, rhs)

        # Our result is the maximum possible profit, the minimum of the two
        # minima we've found (since the minimum of these two values gives the
        # minimum of the overall array), and the maximum of the two maxima.
        maxProfit = max(leftProfit, rightProfit, rightMax - leftMin)
        return (maxProfit, min(leftMin, rightMin), max(leftMax, rightMax))

    # Using our recursive helper function, compute the resulting value.
    profit, _, _ = Recursion(arr, 0, len(arr) - 1)
    return profit

# At this point we've traded our O(n^2)-time, O(1)-space solution for an O(n)-
# time, O(log n) space solution.  But can we do better than this?
#
# To find a better algorithm, we'll need to switch our line of reasoning.
# Rather than using divide-and-conquer, let's see what happens if we use
# dynamic programming.  In particular, let's think about the following problem.
# If we knew the maximum single-sell profit that we could get in just the first
# k array elements, could we use this information to determine what the
# maximum single-sell profit would be in the first k + 1 array elements?  If we
# could do this, we could use the following algorithm:
#
#   Find the maximum single-sell profit to be made in the first 1 elements.
#   For i = 2 to n:
#      Compute the maximum single-sell profit using the first i elements.
#
# How might we do this?  One intuition is as follows.  Suppose that we know the
# maximum single-sell profit of the first k elements.  If we look at k + 1
# elements, then either the maximum profit we could make by buying and selling
# within the first k elements (in which case nothing changes), or we're
# supposed to sell at the (k + 1)st price.  If we wanted to sell at this price
# for a maximum profit, then we would want to do so by buying at the lowest of
# the first k + 1 prices, then selling at the (k + 1)st price.
#
# To accomplish this, suppose that we keep track of the minimum value in the
# first k elements, along with the maximum profit we could make in the first
# k elements.  Upon seeing the (k + 1)st element, we update what the current
# minimum value is, then update what the maximum profit we can make is by
# seeing whether the difference between the (k + 1)st element and the new
# minimum value is.  Note that it doesn't matter what order we do this in; if
# the (k + 1)st element is the smallest element so far, there's no possible way
# that we could increase our profit by selling at that point.
#
# To finish up this algorithm, we should note that given just the first price,
# the maximum possible profit is 0.
#
# This gives the following simple and elegant algorithm for the maximum single-
# sell profit problem:
#
#   Let profit = 0.
#   Let min = arr[0]
#   For k = 1 to length(arr):
#       If arr[k] < min, set min = arr[k]
#       If profit < arr[k] - min, set profit = arr[k] - min
#
# This is short, sweet, and uses only O(n) time and O(1) memory.  The beauty of
# this solution is that we are quite naturally led there by thinking about how
# to update our answer to the problem in response to seeing some new element.
# In fact, we could consider implementing this algorithm as a streaming
# algorithm, where at each point in time we maintain the maximum possible
# profit and then update our answer every time new data becomes available.
#
# The final version of this algorithm is shown here:

def DynamicProgrammingSingleSellProfit(arr):
    # If the array is empty, we cannot make a profit.
    if len(arr) == 0:
        return 0

    # Otherwise, keep track of the best possible profit and the lowest value
    # seen so far.
    profit = 0
    cheapest = arr[0]

    # Iterate across the array, updating our answer as we go according to the
    # above pseudocode.
    for i in range(1, len(arr)):
        # Update the minimum value to be the lower of the existing minimum and
        # the new minimum.
        cheapest = min(cheapest, arr[i])

        # Update the maximum profit to be the larger of the old profit and the
        # profit made by buying at the lowest value and selling at the current
        # price.
        profit = max(profit, arr[i] - cheapest)

    return profit

# To summarize our algorithms, we have seen
#
# Naive:                        O(n ^ 2)   time, O(1)     space
# Divide-and-conquer:           O(n log n) time, O(log n) space
# Optimized divide-and-conquer: O(n)       time, O(log n) space
# Dynamic programming:          O(n)       time, O(1)     space

1
@ FrankQ.- Không gian là cần thiết cho cả hai lệnh gọi đệ quy, nhưng thông thường các lệnh gọi đó được thực hiện lần lượt. Điều này có nghĩa là trình biên dịch có thể sử dụng lại bộ nhớ giữa các lần gọi; khi một cuộc gọi trả về, cuộc gọi tiếp theo có thể sử dụng lại dung lượng của nó. Do đó, bạn chỉ cần bộ nhớ để giữ một cuộc gọi chức năng tại một thời điểm, vì vậy việc sử dụng bộ nhớ tỷ lệ thuận với độ sâu tối đa của ngăn xếp cuộc gọi. Vì đệ quy kết thúc ở mức O (log n) nên chỉ bộ nhớ O (log n) cần được sử dụng. Điều đó có làm sáng tỏ mọi thứ không?
templatetypedef,

Có ai có thể chuyển những thứ này sang Ruby không? Một số đệ quy không hoạt động theo cách giống như trong Python. Ngoài ra các giải pháp này chỉ trả lại lợi nhuận tối đa; họ không trả lại các điểm mảng mang lại lợi nhuận (có thể được sử dụng để báo cáo phần trăm lợi nhuận tăng trong quá khứ)
rcd

Khái niệm lập trình động không thực sự cần thiết để giải thích giải pháp thời gian O (n), nhưng thật tuyệt khi bạn gắn kết tất cả các loại thuật toán này.
Rn222

Làm cách nào bạn có thể xây dựng dựa trên bất kỳ thuật toán phụ O (n ^ 2) nào để tìm tất cả các cặp được sắp xếp theo lợi nhuận?
ferk86

@templatetypedef, chúng ta sẽ thay đổi cách tiếp cận lập trình động như thế nào nếu chúng ta bắt đầu với ngân sách M $ và thay vì một cổ phiếu duy nhất, chúng ta có m cổ phiếu với giá trong n ngày như đã cho? tức là chúng ta khác nhau số đơn vị chứng khoán mua và các dữ liệu chứng khoán có sẵn từ 1 cổ phiếu chứng khoán n (như previosly, chúng tôi đã chỉ cho Google, bây giờ chúng tôi đã cho 5 công ty khác cũng)
Ronak Agrawal

32

Đây là bài toán con tổng lớn nhất với một chút không định hướng. Bài toán con tổng lớn nhất được đưa ra một danh sách các số nguyên có thể dương hoặc âm, tìm tổng lớn nhất của một tập con liền kề của danh sách đó.

Bạn có thể chuyển đổi một cách đáng kể vấn đề này thành vấn đề đó bằng cách lấy lãi hoặc lỗ giữa các ngày liên tiếp. Vì vậy, bạn sẽ chuyển đổi danh sách giá cổ phiếu, ví dụ: [5, 6, 7, 4, 2]thành danh sách lãi / lỗ, ví dụ [1, 1, -3, -2],. Bài toán tổng của dãy con sau đó khá dễ giải quyết: Tìm dãy con có tổng các phần tử lớn nhất trong một mảng


1
Tôi không nghĩ rằng nó hoàn toàn hoạt động theo cách này, vì nếu bạn mua cổ phiếu vào một ngày đầu tiên nào đó, bạn sẽ không tích lũy lợi ích của delta từ ngày hôm trước. Hay đây không phải là vấn đề trong cách tiếp cận này?
templatetypedef

1
@templatetypedef, đó là lý do tại sao bạn theo dõi tổng lớn nhất và tổng chuỗi hiện tại. Khi tổng chuỗi hiện tại xuống dưới 0, bạn biết bạn sẽ không kiếm được tiền với chuỗi đó và bạn có thể bắt đầu lại từ đầu. Bằng cách theo dõi số tiền lớn nhất, bạn sẽ tự động tìm thấy những ngày mua / bán tốt nhất.
MSN

6
@templatetypedef, tình cờ, bạn làm điều tương tự trong câu trả lời của mình.
MSN

16

Tôi không thực sự chắc chắn tại sao đây được coi là một câu hỏi lập trình động. Tôi đã thấy câu hỏi này trong sách giáo khoa và hướng dẫn thuật toán sử dụng thời gian chạy O (n log n) và O (log n) cho không gian (ví dụ: Phần tử của Phỏng vấn lập trình). Nó có vẻ như là một vấn đề đơn giản hơn nhiều so với những gì mọi người đang làm.

Điều này hoạt động bằng cách theo dõi lợi nhuận tối đa, giá mua tối thiểu và do đó, giá mua / bán tối ưu. Khi đi qua từng phần tử trong mảng, nó sẽ kiểm tra xem liệu phần tử đã cho có nhỏ hơn giá mua tối thiểu hay không. Nếu đúng như vậy, chỉ số giá mua tối thiểu, ( min), được cập nhật để trở thành chỉ số của phần tử đó. Ngoài ra, đối với mỗi phần tử, becomeABillionairethuật toán kiểm tra xem arr[i] - arr[min](chênh lệch giữa phần tử hiện tại và giá mua tối thiểu) có lớn hơn lợi nhuận hiện tại hay không. Nếu đúng như vậy, lợi nhuận được cập nhật thành chênh lệch đó và đặt thành mua arr[min]và bán arr[i].

Chạy trong một lần vượt qua.

static void becomeABillionaire(int arr[]) {
    int i = 0, buy = 0, sell = 0, min = 0, profit = 0;

    for (i = 0; i < arr.length; i++) {
        if (arr[i] < arr[min])
            min = i;
        else if (arr[i] - arr[min] > profit) {
            buy = min; 
            sell = i;
            profit = arr[i] - arr[min];
        }

    }

    System.out.println("We will buy at : " + arr[buy] + " sell at " + arr[sell] + 
            " and become billionaires worth " + profit );

}

Đồng tác giả: https://stackoverflow.com/users/599402/ephraim


2

Vấn đề giống với chuỗi con tối đa
mà tôi đã giải quyết bằng cách sử dụng Lập trình động. Theo dõi hiện tại và trước đó (Lợi nhuận, ngày mua và ngày bán) Nếu hiện tại cao hơn trước đó thì hãy thay thế trước đó bằng hiện tại.

    int prices[] = { 38, 37, 35, 31, 20, 24, 35, 21, 24, 21, 23, 20, 23, 25, 27 };

    int buyDate = 0, tempbuyDate = 0;
    int sellDate = 0, tempsellDate = 0; 

    int profit = 0, tempProfit =0;
    int i ,x = prices.length;
    int previousDayPrice = prices[0], currentDayprice=0;

    for(i=1 ; i<x; i++ ) {

        currentDayprice = prices[i];

        if(currentDayprice > previousDayPrice ) {  // price went up

            tempProfit = tempProfit + currentDayprice - previousDayPrice;
            tempsellDate = i;
        }
        else { // price went down 

            if(tempProfit>profit) { // check if the current Profit is higher than previous profit....

                profit = tempProfit;
                sellDate = tempsellDate;
                buyDate = tempbuyDate;
            } 
                                     // re-intialized buy&sell date, profit....
                tempsellDate = i;
                tempbuyDate = i;
                tempProfit =0;
        }
        previousDayPrice = currentDayprice;
    }

    // if the profit is highest till the last date....
    if(tempProfit>profit) {
        System.out.println("buydate " + tempbuyDate + " selldate " + tempsellDate + " profit " + tempProfit );
    }
    else {
        System.out.println("buydate " + buyDate + " selldate " + sellDate + " profit " + profit );
    }   

2

đây là giải pháp Java của tôi:

public static void main(String[] args) {
    int A[] = {5,10,4,6,12};

    int min = A[0]; // Lets assume first element is minimum
    int maxProfit = 0; // 0 profit, if we buy & sell on same day.
    int profit = 0;
    int minIndex = 0; // Index of buy date
    int maxIndex = 0; // Index of sell date

    //Run the loop from next element
    for (int i = 1; i < A.length; i++) {
        //Keep track of minimum buy price & index
        if (A[i] < min) {
            min = A[i];
            minIndex = i;
        }
        profit = A[i] - min;
        //If new profit is more than previous profit, keep it and update the max index
        if (profit > maxProfit) {
            maxProfit = profit;
            maxIndex = i;
        }
    }
    System.out.println("maxProfit is "+maxProfit);
    System.out.println("minIndex is "+minIndex);
    System.out.println("maxIndex is "+maxIndex);     
}

@Nitiraj, vâng, giải pháp này đúng nhưng tôi yêu cầu bạn vui lòng đọc câu trả lời do templatetypedef cung cấp, vì trong câu trả lời do templatetypedef cung cấp, tất cả các giải pháp khả thi đều được đề cập bao gồm cả giải pháp được đăng bởi Rohit. Giải pháp của Rohit thực sự là một triển khai của giải pháp cuối cùng với O (n) bằng cách sử dụng lập trình động được đề cập trong câu trả lời do templatetypedef cung cấp.
nits.kk

1
Giả sử mảng của bạn là int A [] = {5, 4, 6, 7, 6, 3, 2, 5}; Sau đó, theo logic của bạn, bạn sẽ mua ở chỉ số 6 và sau đó bán nó ở chỉ số 3. Điều đó là sai. Bạn không thể bán trong quá khứ. Chỉ số bán phải lớn hơn chỉ số mua.
developer747,

1
Giải pháp trên là "gần như đúng". nhưng nó in chỉ số tối thiểu tuyệt đối thay vì chỉ số của giá "mua". Để sửa, bạn cần một biến khác, giả sử minBuyIndex mà bạn chỉ cập nhật bên trong khối "if (profit> maxProfit)" và in ra.
javabrew

1

Tôi đã đưa ra một giải pháp đơn giản - mã mang tính tự giải thích nhiều hơn. Đó là một trong những câu hỏi lập trình động.

Mã không quan tâm đến việc kiểm tra lỗi và các trường hợp cạnh. Nó chỉ là một mẫu để đưa ra ý tưởng về logic cơ bản để giải quyết vấn đề.

namespace MaxProfitForSharePrice
{
    class MaxProfitForSharePrice
    {
        private static int findMax(int a, int b)
        {
            return a > b ? a : b;
        }

        private static void GetMaxProfit(int[] sharePrices)
        {
            int minSharePrice = sharePrices[0], maxSharePrice = 0, MaxProft = 0;
            int shareBuyValue = sharePrices[0], shareSellValue = sharePrices[0];

            for (int i = 0; i < sharePrices.Length; i++)
            {
                if (sharePrices[i] < minSharePrice )
                {
                    minSharePrice = sharePrices[i];
                    // if we update the min value of share, we need to reset the Max value as 
                    // we can only do this transaction in-sequence. We need to buy first and then only we can sell.
                    maxSharePrice = 0; 
                }
                else 
                {
                    maxSharePrice = sharePrices[i];
                }

                // We are checking if max and min share value of stock are going to
                // give us better profit compare to the previously stored one, then store those share values.
                if (MaxProft < (maxSharePrice - minSharePrice))
                {
                    shareBuyValue = minSharePrice;
                    shareSellValue = maxSharePrice;
                }

                MaxProft = findMax(MaxProft, maxSharePrice - minSharePrice);
            }

            Console.WriteLine("Buy stock at ${0} and sell at ${1}, maximum profit can be earned ${2}.", shareBuyValue, shareSellValue, MaxProft);
        }

        static void Main(string[] args)
        {
           int[] sampleArray = new int[] { 1, 3, 4, 1, 1, 2, 11 };
           GetMaxProfit(sampleArray);
            Console.ReadLine();
        }
    }
}

1
public static double maxProfit(double [] stockPrices)
    {
        double initIndex = 0, finalIndex = 0;

        double tempProfit = list[1] - list[0];
        double maxSum = tempProfit;
        double maxEndPoint = tempProfit;


        for(int i = 1 ;i<list.length;i++)
        {
            tempProfit = list[ i ] - list[i - 1];;

            if(maxEndPoint < 0)
            {
                maxEndPoint = tempProfit;
                initIndex = i;
            }
            else
            {
                maxEndPoint += tempProfit;
            }

            if(maxSum <= maxEndPoint)
            {
                maxSum = maxEndPoint ;
                finalIndex = i;
            }
        }
        System.out.println(initIndex + " " + finalIndex);
        return maxSum;

    }

Đây là giải pháp của tôi. sửa đổi thuật toán dãy con tối đa. Giải quyết vấn đề trong O (n). Tôi nghĩ rằng nó không thể được thực hiện nhanh hơn.


1

Đây là một vấn đề thú vị, bởi vì nó có vẻ khó, nhưng suy nghĩ cẩn thận sẽ tạo ra một giải pháp nhẹ nhàng, nhẹ nhàng.

Như đã được lưu ý, nó có thể được giải quyết bạo lực trong thời gian O (N ^ 2). Đối với mỗi mục nhập trong mảng (hoặc danh sách), hãy lặp lại tất cả các mục nhập trước đó để lấy giá trị tối thiểu hoặc tối đa tùy thuộc vào vấn đề là tìm kiếm lợi nhuận hay tổn thất lớn nhất.

Đây là cách suy nghĩ về một giải pháp trong O (N): mỗi mục nhập đại diện cho một giá trị tối đa (hoặc tối thiểu) mới có thể. Sau đó, tất cả những gì chúng ta cần làm là lưu giá trị tối thiểu (hoặc tối đa) trước đó và so sánh chênh lệch với giá trị hiện tại và giá trị tối thiểu (hoặc tối đa) trước đó. Dễ như ăn bánh.

Đây là mã, trong Java như một thử nghiệm JUnit:

import org.junit.Test;

public class MaxDiffOverSeriesProblem {

    @Test
    public void test1() {
        int[] testArr = new int[]{100, 80, 70, 65, 95, 120, 150, 75, 95, 100, 110, 120, 90, 80, 85, 90};

        System.out.println("maxLoss: " + calculateMaxLossOverSeries(testArr) + ", maxGain: " + calculateMaxGainOverSeries(testArr));
    }

    private int calculateMaxLossOverSeries(int[] arr) {
        int maxLoss = 0;

        int idxMax = 0;
        for (int i = 0; i < arr.length; i++) {
            if (arr[i] > arr[idxMax]) {
                idxMax = i;
            }

            if (arr[idxMax] - arr[i] > maxLoss) {
                maxLoss = arr[idxMax] - arr[i];
            }           
        }

        return maxLoss;
    }

    private int calculateMaxGainOverSeries(int[] arr) {
        int maxGain = 0;

        int idxMin = 0;
        for (int i = 0; i < arr.length; i++) {
            if (arr[i] < arr[idxMin]) {
                idxMin = i;
            }

            if (arr[i] - arr[idxMin] > maxGain) {
                maxGain = arr[i] - arr[idxMin];
            }           
        }

        return maxGain;
    }

}

Trong trường hợp tính toán khoản lỗ lớn nhất, chúng tôi theo dõi mức tối đa trong danh sách (giá mua) cho đến mục nhập hiện tại. Sau đó, chúng tôi tính toán sự khác biệt giữa mục nhập tối đa và mục nhập hiện tại. Nếu max - current> maxLoss, thì chúng tôi giữ khác biệt này là maxLoss mới. Vì chỉ số tối đa được đảm bảo nhỏ hơn chỉ số hiện tại, chúng tôi đảm bảo rằng ngày "mua" nhỏ hơn ngày "bán".

Trong trường hợp tính toán lợi nhuận lớn nhất, mọi thứ đều đảo ngược. Chúng tôi theo dõi số phút tối thiểu trong danh sách cho đến mục nhập hiện tại. Chúng tôi tính toán sự khác biệt giữa mục nhập tối thiểu và hiện tại (đảo ngược thứ tự trong phép trừ). Nếu hiện tại - min> maxGain, thì chúng tôi giữ khác biệt này làm maxGain mới. Một lần nữa, chỉ số của 'mua' (tối thiểu) đứng trước chỉ số của hiện tại ('bán').

Chúng tôi chỉ cần theo dõi maxGain (hoặc maxLoss) và chỉ số tối thiểu hoặc tối đa, nhưng không phải cả hai và chúng tôi không cần so sánh các chỉ số để xác nhận rằng 'mua' ít hơn 'bán', vì chúng tôi có được điều này một cách tự nhiên.


1

Lợi nhuận bán một lần tối đa, giải pháp O (n)

function stocks_n(price_list){
    var maxDif=0, min=price_list[0]

    for (var i in price_list){
        p = price_list[i];
        if (p<min)
            min=p
        else if (p-min>maxDif)
                maxDif=p-min;
   }

    return maxDif
}

Đây là một dự án thực hiện kiểm tra độ phức tạp thời gian trên phương pháp tiếp cận o (N) so với o (n ^ 2) trên tập dữ liệu ngẫu nhiên trên 100 nghìn int. O (n ^ 2) mất 2 giây, trong khi O (n) mất 0,01 giây

https://github.com/gulakov/complexity.js

function stocks_n2(ps){
    for (maxDif=0,i=_i=0;p=ps[i++];i=_i++)
        for (;p2=ps[i++];)
            if (p2-p>maxDif)
                maxDif=p2-p
    return maxDif
}

Đây là cách tiếp cận chậm hơn, o (n ^ 2) lặp lại qua các ngày còn lại cho mỗi ngày, vòng lặp kép.


1

Câu trả lời được bình chọn nhiều nhất không cho phép các trường hợp trong đó lợi nhuận tối đa là âm và nên được sửa đổi để cho phép các trường hợp đó. Người ta có thể làm như vậy bằng cách giới hạn phạm vi của vòng lặp là (len (a) - 1) và thay đổi cách xác định lợi nhuận bằng cách dịch chuyển chỉ số này.

def singSellProfit(a):
profit = -max(a)
low = a[0]

for i in range(len(a) - 1):
    low = min(low, a[i])
    profit = max(profit, a[i + 1] - low)
return profit

So sánh phiên bản này của hàm với phiên bản trước của mảng:

s = [19,11,10,8,5,2]

singSellProfit(s)
-1

DynamicProgrammingSingleSellProfit(s)
0

0
static void findmaxprofit(int[] stockvalues){
    int buy=0,sell=0,buyingpoint=0,sellingpoint=0,profit=0,currentprofit=0;
    int finalbuy=0,finalsell=0;
    if(stockvalues.length!=0){
        buy=stockvalues[0];
    }           
    for(int i=1;i<stockvalues.length;i++){  
        if(stockvalues[i]<buy&&i!=stockvalues.length-1){                
            buy=stockvalues[i];
            buyingpoint=i;
        }               
        else if(stockvalues[i]>buy){                
            sell=stockvalues[i];
            sellingpoint=i;
        }
        currentprofit=sell-buy;         
        if(profit<currentprofit&&sellingpoint>buyingpoint){             
            finalbuy=buy;
            finalsell=sell;
            profit=currentprofit;
        }

    }
    if(profit>0)
    System.out.println("Buy shares at "+finalbuy+" INR and Sell Shares "+finalsell+" INR and Profit of "+profit+" INR");
    else
        System.out.println("Don't do Share transacations today");
}

0

Một khả năng để xác định lợi nhuận tối đa có thể là theo dõi các phần tử tối thiểu bên trái và tối đa bên phải trong mảng tại mỗi chỉ mục trong mảng. Sau đó, khi bạn duyệt qua giá cổ phiếu, trong bất kỳ ngày nào bạn sẽ biết giá thấp nhất cho đến ngày đó và bạn cũng sẽ biết giá tối đa sau (và bao gồm cả) ngày đó.

Ví dụ, hãy xác định a min_arrmax_arr, với mảng đã cho là arr. Chỉ mục itrong min_arrsẽ là phần tử tối thiểu arrcho tất cả các chỉ số <= i(bên trái và bao gồm cả i). Chỉ mục itrong max_arrsẽ là phần tử tối đa arrcho tất cả các chỉ số >= i(bên phải và bao gồm cả i). Sau đó, bạn có thể tìm thấy sự khác biệt lớn nhất giữa các phần tử tương ứng trong max_arrvà `min_arr ':

def max_profit(arr)
   min_arr = []
   min_el = arr.first
   arr.each do |el|
       if el < min_el
           min_el = el
           min_arr << min_el
       else
           min_arr << min_el
       end
   end

   max_arr = []
   max_el = arr.last
   arr.reverse.each do |el|
       if el > max_el
           max_el = el
           max_arr.unshift(max_el)
       else
           max_arr.unshift(max_el)
       end

   end

   max_difference = max_arr.first - min_arr.first
   1.upto(arr.length-1) do |i|
        max_difference = max_arr[i] - min_arr[i] if max_difference < max_arr[i] - min_arr[i]  
   end

   return max_difference 
end

Điều này sẽ chạy trong O (n) thời gian, nhưng tôi tin rằng nó sử dụng rất nhiều dung lượng.


0

Đây là sự khác biệt lớn nhất giữa hai phần tử trong mảng và đây là giải pháp của tôi:

Độ phức tạp thời gian O (N) độ phức tạp không gian O (1)

    int[] arr   =   {5, 4, 6 ,7 ,6 ,3 ,2, 5};

    int start   =   0;
    int end     =   0;
    int max     =   0;
    for(int i=1; i<arr.length; i++){
        int currMax =   arr[i] - arr[i-1];
        if(currMax>0){
            if((arr[i] -arr[start])>=currMax && ((arr[i] -arr[start])>=(arr[end] -arr[start]))){

                 end    =   i;
            }
            else if(currMax>(arr[i] -arr[start]) && currMax >(arr[end] - arr[start])){
                start   =   i-1;
                end =   i;
            }
        }
    }
    max =   arr[end] - arr[start];
    System.out.println("max: "+max+" start: "+start+" end: "+end);

0

Sau khi thất bại điều này trong một kỳ thi viết mã trực tiếp cho vị trí kỹ sư giải pháp FB, tôi đã phải giải quyết nó trong một bầu không khí mát mẻ yên tĩnh, vì vậy đây là 2 xu của tôi:

var max_profit = 0;
var stockPrices = [23,40,21,67,1,50,22,38,2,62];

var currentBestBuy = 0; 
var currentBestSell = 0;
var min = 0;

for(var i = 0;i < (stockPrices.length - 1) ; i++){
    if(( stockPrices[i + 1] - stockPrices[currentBestBuy] > max_profit) ){
        max_profit = stockPrices[i + 1] - stockPrices[currentBestBuy];
        currentBestSell = i + 1;  
    }
    if(stockPrices[i] < stockPrices[currentBestBuy]){
            min = i;
        }
    if( max_profit < stockPrices[i + 1] - stockPrices[min] ){
        max_profit = stockPrices[i + 1] - stockPrices[min];
        currentBestSell = i + 1;
        currentBestBuy = min;
    }
}

console.log(currentBestBuy);
console.log(currentBestSell);
console.log(max_profit);

Câu trả lời chỉ có mã không được khuyến khích.
Pritam Banerjee

0

Câu trả lời duy nhất thực sự trả lời câu hỏi là @akash_magoon (và theo một cách đơn giản như vậy!), Nhưng nó không trả về đối tượng chính xác được chỉ định trong câu hỏi. Tôi đã cấu trúc lại một chút và có câu trả lời của tôi trong PHP chỉ trả về những gì được hỏi:

function maximizeProfit(array $dailyPrices)
{
    $buyDay = $sellDay = $cheaperDay = $profit = 0;

    for ($today = 0; $today < count($dailyPrices); $today++) {
        if ($dailyPrices[$today] < $dailyPrices[$cheaperDay]) {
            $cheaperDay = $today;
        } elseif ($dailyPrices[$today] - $dailyPrices[$cheaperDay] > $profit) {
            $buyDay  = $cheaperDay;
            $sellDay = $today;
            $profit   = $dailyPrices[$today] - $dailyPrices[$cheaperDay];
        }
    }
    return [$buyDay, $sellDay];
}

0

Một giải pháp gọn gàng:

+ (int)maxProfit:(NSArray *)prices {
    int maxProfit = 0;

    int bestBuy = 0;
    int bestSell = 0;
    int currentBestBuy = 0;

    for (int i= 1; i < prices.count; i++) {
        int todayPrice = [prices[i] intValue];
        int bestBuyPrice = [prices[currentBestBuy] intValue];
        if (todayPrice < bestBuyPrice) {
            currentBestBuy = i;
            bestBuyPrice = todayPrice;
        }

        if (maxProfit < (todayPrice - bestBuyPrice)) {
            bestSell = i;
            bestBuy = currentBestBuy;
            maxProfit = (todayPrice - bestBuyPrice);
        }
    }

    NSLog(@"Buy Day : %d", bestBuy);
    NSLog(@"Sell Day : %d", bestSell);

    return maxProfit;
}

0
def get_max_profit(stock):
    p=stock[0]
    max_profit=0
    maxp=p
    minp=p
    for i in range(1,len(stock)):
        p=min(p,stock[i])
        profit=stock[i]-p
        if profit>max_profit:
            maxp=stock[i]
            minp=p
            max_profit=profit
    return minp,maxp,max_profit



stock_prices = [310,315,275,295,260,270,290,230,255,250]
print(get_max_profit(stock_prices))

Chương trình này trong python3 có thể trả lại giá mua và giá bán sẽ tối đa hóa lợi nhuận, được tính bằng Độ phức tạp thời gian của O (n)Độ phức tạp không gian của O (1) .


0

Đây là giải pháp của tôi

public static int maxProfit(List<Integer> in) {
    int min = in.get(0), max = 0;
    for(int i=0; i<in.size()-1;i++){

        min=Math.min(min, in.get(i));

        max = Math.max(in.get(i) - min, max);
     }

     return max;
 }
}

-1

Đối với tất cả các câu trả lời theo dõi các phần tử tối thiểu và tối đa, giải pháp đó thực sự là một nghiệm O (n ^ 2). Điều này là do cuối cùng nó phải được kiểm tra xem liệu cực đại có xảy ra sau giá trị tối thiểu hay không. Trong trường hợp không, cần phải lặp lại thêm cho đến khi điều kiện đó được đáp ứng và điều này để lại trường hợp xấu nhất là O (n ^ 2). Và nếu bạn muốn bỏ qua các lần lặp thêm thì cần phải có thêm nhiều dung lượng. Dù bằng cách nào, không-không so với giải pháp lập trình động

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.