简体   繁体   中英

Reaching the end of an array with a sequence of odd/even jumps

I'm trying to understand a Python solution to a programming puzzle that seems to use dynamic programming. I am able to follow most of the solution, but I'm struggling a bit to really formalize the solution.

The problem is stated as follows:

You are given an integer array A . From some starting index, you can make a series of jumps. The (1st, 3rd, 5th, ...) jumps in the series are called "odd" numbered jumps, and the (2nd, 4th, 6th, ...) jumps in the series are called even numbered jumps.

You may jump forward from index i to index j (with i < j ) in the following way:

  • During odd numbered jumps (ie. jumps 1, 3, 5, ...), you jump to the index j such that A[i] <= A[j] and A[j] is the smallest possible value. If there are multiple such indexes j , you can only jump to the smallest such index j.

  • During even numbered jumps (ie. jumps 2, 4, 6, ...), you jump to the index j such that A[i] >= A[j] and A[j] is the largest possible value. If there are multiple such indexes j , you can only jump to the smallest such index j .

  • It may be the case that for some index i are no legal jumps.

A starting index is good if, starting from that index, you can reach the end of the array by jumping some number of times (possibly 0 or more than once.)

We ask that you return the number of good starting indices.

Below I show a solution in Python to this problem that a few authors have published. I understand how it builds oddnext and evennext and what these arrays technically hold (the index location one jumps to from that index with an odd or even jump). What I don't understand is how the last loop works, and what's the precise dynamic programming formulation behind it.

def odd_even_jumps(self, A):
    N = len(A)

    def make(B):
        ans = [None] * N
        stack = []  # invariant: stack is decreasing
        for i in B:
            while stack and i > stack[-1]:
                ans[stack.pop()] = i
            stack.append(i)
        return ans

    B = sorted(range(N), key = lambda i: A[i])
    oddnext = make(B)
    B.sort(key = lambda i: -A[i])
    evennext = make(B)

    odd = [False] * N
    even = [False] * N
    odd[N-1] = even[N-1] = True

    # Why does this loop start from the end? 

    for i in range(N-2, -1, -1):
        if oddnext[i] is not None:
            odd[i] = even[oddnext[i]]
        if evennext[i] is not None:
            even[i] = odd[evennext[i]]

    return sum(odd)

The first part of the algorithm identifies for any given index of the input array, where do you jump to to with an odd ( oddNext ) or even ( evenNext ) jump. Some indices are populated with None because you can't make any legal (even or odd) jumps from them.

Once you have this information, to answer your question, please note the following:

  • Any index that can legally jump to a good index must be a good index (you can "re-use" the solution for that good index to reach the end)
  • The last index of the input array is always a "good index" (ie you reached the end already)

As a result, you can proceed backwards from the end of the array to identify good indices by checking if they would jump forward to an index that you already identified as a good (or not good) index.

Note that this is a dynamic programming calculation, since you are establishing a recurrent relationship between good indices and exploiting it as you go backwards in the array (ie you are solving the dynamic programming subproblems in what's called a "bottom up" fashion, as per their dependency structure).

One thing to note is that this algorithm computes if an index is good or not for every location and every possible type of jump (ie we fully populate the odd and even arrays). That's because, as you iterate in the dynamic programming calculations, different starting points of the array may need to make an odd or even jump from that index, even if , when starting from any particular index, you can only do so with an odd jump as per the problem statement.

That's fine. In dynamic programming one often solves all subproblems even if the solution to the original problem doesn't rely on all of them. Note that this is also why in the end you only care about which indices in the odd array are good indices. The even array was just part of the dynamic programming scaffolding to populate the odd array.

every iteration you check if this index has an odd jump if it does it check the jump destination if you where in the destination and had an even jump could you reach the end if so this index value in odd should be true

then you check the same for even (do i have an even jump? its dest is index with true in odd?, set the even value true)

then you return the number of odd true values because your first jump is odd

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM