3

Hello, everyone, this is my first post here.

So today during my university class, our professor gave us a task to write an algorithm:

Write a function that returns the count for the amount of steps you need to make in order to get the most score in a board game:

Rules of the game:

  • you throw a dice and move accordingly (1-6 steps).
  • the amount of tiles on the board can range anywhere between 2 - 99 999.
  • when you step on a tile you receive or lose points (the points on each tile vary from -99 999, to 99 999).
  • if you are at the end of the board and your dice throw gets you out of it's boundaries, you don't move.

My approach

It's sort of a greedy algorithm:

  • count for each step if it's above or equal to 0,
  • if it's negative, check for the next 6 tiles and move to the one with the highest score, to lose the least amount of points.

I realized that my approach is wrong, after I imagined this example:

flaw in my approach

So imagine an array of {1, -40, -40, -40, -40, -1, -38, -40, -40, -40, -40, -40, 1}

My greedy algorithm starts at 1 and sees four -40's, one -38 and one -1. It chooses -1 because it's the best option, but now we will end up with a result of: 1 + (-1) + (-38) + 1 = -37, however if we choose -38 instead of -1, we would end up with: 1 + (-38) + 1 = -36.

This is just a simple example of what the problems could be, I imagine I'd have to check for every path possible, because greedy algorithms don't check for the best path out there, only the best applicable for some particular moment.

I was wondering if a graph with all the possibilities could be an option here, but if we had an array of only negative numbers, then we would end up with a graph with maximum size of something around (99999^6?), which would result in taking up too much memory.

I'm a newbie and I've ran out of ideas. Could anyone point me towards the right direction?

  • Was it specified that you must use a greedy algorithm? – Alex Dec 06 '21 at 19:21
  • As you determined, a greedy approach won't always give a correct answer. However, a recursive/dynamic-programming based approach might. Given your array of scores `A`, define `best(A, i)` to be the best score you can get starting at index `i`. You need to solve `best(A, 0)`, and because it's possible to step 1-6 steps, `best(A, 0) = A[0] + max(best(A, k))` for `1 <= k <= 5`. From here, you can define the general recurrence for any arbitrary index of `A`. – wLui155 Dec 06 '21 at 19:27
  • Updated my answer to include a greedy solution. – Shridhar R Kulkarni Dec 06 '21 at 19:48
  • Maybe keep a history of the number of steps and best score of each item in the array as we iterate it. So for each new item in the array, we find the best score from adding on steps to any one of the last six spaces. This at most uses an int and a long per array size, but you could delete any results older than six spaces if you were really trying to minimize memory use. – Tenfour04 Dec 06 '21 at 19:50
  • No I don't need to use a greedy algorithm, it's just my approach to this problem. @Alex – FilthyProgrammer96 Dec 06 '21 at 21:17

1 Answers1

3

Oh, wait! Greedy is possible here using a maximum heap.

Idea:

Max heap is a data structure which maintains the maximum element at the top of the structure. If we keep 6 values that could come up after the dice roll in maximum heap, we can get the maximum possible value that dice can roll.

Let us consider your example array {1, -40, -40, -40, -40, -1, -38, -40, -40, -40, -40, -40, 1}.

Push first six values that dice can roll in the max heap. So, max heap contains the elements {<1, 0>, <-40, 1>, <-40, 2>, <-40, 3>, <-40, 4>, <-1, 5>} (not in max heap order).

Here <top of the max heap>.value = 1 and <top of the max heap>.index = 0.

Now, max_score = 1.

After popping the max heap and adding the next element from the array, max heap would contain following elements {<-40, 1>, <-40, 2>, <-40, 3>, <-40, 4>, <-1, 5>, <-38, 6>} (not in max heap order).

Similarly, you can work out for remaining elements in the input array using the below algorithm.

Algorithm:

max_score, curr_index, steps = 0
Push input_array[0..max(5, size_of_input_array - 1)] values in a max heap in the form <value, index in the array>
i = 6

while(max heap is not empty):
    while(max heap is not empty and <top of the max heap>.index < curr_index):
        pop the max heap
    if(max heap is not empty):
        max_score += <top of the max heap>.value
        curr_index = <top of the max heap>.index
        steps++
        pop the max heap
    if(i < size_of_input_array):
        push the <input_array[i], i> into the max heap
        i++
return max_score, steps

Time complexity: O(size_of_input_array * log(6)) => O(size_of_input_array)
Space complexity: O(6) => Constant

Not giving out the full working code as it is your assignment but the above algorithm is not less than code.

You can try with Depth First Search, Dynamic Programming as well. But the above greedy solution is asymptotically the most efficient approach.

Hope that helped you. I loved solving it. Thanks!

Shridhar R Kulkarni
  • 6,653
  • 3
  • 37
  • 57