Maximum Sum Subarray of Size K (easy)
Resources for finding the maximum sum among all subarrays
Real-world problems
A variant of the “maximum sum subarray of size K” problem comes up when designing a feature for the computer player in the card game Fizzle.
The winning strategy in a card game
In Fizzle, the dealer shuffles the deck and spreads out all the cards facing upwards in a linear fashion. Then, players take turns rolling a die. Suppose the number rolled is . Players will then take turns to remove cards from the deck, while respecting this constraint: they may only pick cards from either the deck’s right or left side.
Each player’s goal is to pick out the cards with maximum points. Each numbered card carries points corresponding to its number, and the faced cards, jack, queen, king, and ace, carry , , , and points, respectively.
Our task is to create a feature for Fizzle’s computer player. We will be given the deck’s current state and the number the player rolled. We need to determine the maximum score that the player can obtain on that turn.
Let’s take a look at the following example:
In the example above, the player picked the cards , , , and in the given order, to obtain the maximum points possible, that is .
We will receive this deck of cards in the form of an array, such as {5, 3, 4, 8, 2, 3, 2, 6, 3}
. The number obtained from rolling the die will be given as an integer, such as 4.
Our function needs to return the maximum number of points obtained.
Encourage the students to see the link between this problem and the sliding window pattern. Can they see a window, that is, a contiguous set of cards (or numbers representing cards) in this problem?
Some questions to think about:
- What is the size of the window?
- Are we interested in the cards inside the window or outside?
- Is it an individual property we are interested in or an aggregate one?
- Is it a simple linear array, or a circular array?
Dry-run templates
This is the implementation of the more efficient solution provided for the problem posed in the lesson:
def max_sub_array_of_size_k(k, arr):max_sum , window_sum = 0, 0window_start = 0for window_end in range(len(arr)):window_sum += arr[window_end] # add the next element# slide the window, we don't need to slide if we've not hit the required window size of 'k'if window_end >= k-1:max_sum = max(max_sum, window_sum)window_sum -= arr[window_start] # subtract the element going outwindow_start += 1 # slide the window aheadreturn max_sumdef main():print("Maximum sum of a subarray of size K: " + str(max_sub_array_of_size_k(3, [2, 1, 5, 1, 3, 2])))print("Maximum sum of a subarray of size K: " + str(max_sub_array_of_size_k(2, [2, 3, 4, 1, 5])))main()
Students may be encouraged to run through the provided solution with the following sample inputs:
Sample Input #1
K = 3, Array: {2, 1, 5, 3, 2}
Iteration number | Line number | windowStart | windowEnd | windowSum | maxSum |
- | 2 - 3 | 0 | - | 0 | 0 |
1 | 5 - 6 | 0 | 0 | 2 | 0 |
2 | 5 - 6 | 0 | 1 | 3 | 0 |
3 | 5 - 6 | 0 | 2 | 8 | 0 |
3 | 8 - 11 | 1 | 2 | 6 | 8 |
4 | 5 - 6 | 1 | 3 | 7 | 8 |
4 | 8 - 11 | 2 | 3 | 6 | 8 |
5 | 5 - 6 | 2 | 4 | 9 | 8 |
5 | 8 - 11 | 3 | 4 | 4 | 9 |
6 | 5 - 6 | 3 | 5 | 6 | 9 |
6 | 8 - 11 | 4 | 5 | 5 | 9 |
Sample Input #2
K = 2, Array: {2, 3, 4, 1, 5}
Iteration number | Line number | windowStart | windowEnd | windowSum | maxSum |
- | 2 - 3 | 0 | - | 0 | 0 |
1 | 5 - 6 | 0 | 0 | 2 | 0 |
2 | 5 - 6 | 0 | 1 | 5 | 0 |
2 | 8 - 11 | 1 | 1 | 3 | 5 |
3 | 5 - 6 | 1 | 2 | 7 | 5 |
3 | 8 - 11 | 2 | 2 | 4 | 7 |
4 | 5 - 6 | 2 | 3 | 5 | 7 |
4 | 8 - 11 | 3 | 3 | 1 | 7 |
5 | 5 - 6 | 3 | 4 | 6 | 7 |
5 | 8 - 11 | 4 | 4 | 5 | 7 |
Step-by-step solution construction
We have already learned how to slide a window of a size of our choice across an array of values, as well as how to calculate aggregate values such as the sum and average of the current window.
Run the code below to verify that the correct window is identified and that the sum calculated for each window is correct:
# -*- coding: utf-8 -*-def max_sub_array_of_size_k(k, arr):window_sum = 0window_start = 0for window_end in range(len(arr)):print("\nLoop index: " + str(window_end))window_sum += arr[window_end] # add the next element# slide the window, we don't need to slide if we've not hit the required window size of 'k'if window_end >= k-1:print("Window starts at index " + str(window_start) + ": " +print_array_with_slice_markers(arr, window_start, window_end, '«', '»'))print("Sum of current window: " + str(window_sum))window_sum -= arr[window_start] # subtract the element going outwindow_start += 1 # slide the window aheadreturn 0def print_array_with_slice_markers(arr, slice_start, slice_end, slice_opening, slice_ending):out = "[";for i in range(len(arr)):if slice_start == i:out += slice_openingout += str(arr[i]) + (slice_ending if slice_end == i else "") + ", ";out = out[:-2];out += "]";return outdef main():window_sizes = [3, 2];input_arrays = [[2, 1, 5, 1, 3, 2], [2, 3, 4, 1, 5]];for i in range(len(window_sizes)):print("Window size: " + str(window_sizes[i]) + ", input array: " + str(input_arrays[i]))print("Maximum sum of a subarray of size K = " + str(window_sizes[i]) + ": " + str(max_sub_array_of_size_k(window_sizes[i], input_arrays[i])))print("-------------------------------------------------------------------------------------------------------\n")main()
Now, all we need to do is to check if each newly calculated sum is greater than the largest sum already encountered. If it is, it becomes the new maximum sum.
Let’s use a new variable, max_sum
, to store the maximum sum calculated so far and add the logic (see lines 4 and 13) to update its value:
# -*- coding: utf-8 -*-def max_sub_array_of_size_k(k, arr):max_sum , window_sum = 0, 0window_start = 0star = ''for window_end in range(len(arr)):print("\nLoop index: " + str(window_end))window_sum += arr[window_end] # add the next element# slide the window, we don't need to slide if we've not hit the required window size of 'k'if window_end >= k-1:star = '*' if max_sum < window_sum else ''max_sum = max(max_sum, window_sum)print("Window starts at index " + str(window_start) + ": " +print_array_with_slice_markers(arr, window_start, window_end, '«', '»'))print("Sum of current window: " + str(window_sum) + star)window_sum -= arr[window_start] # subtract the element going outwindow_start += 1 # slide the window aheadreturn max_sumdef print_array_with_slice_markers(arr, slice_start, slice_end, slice_opening, slice_ending):out = "[";for i in range(len(arr)):if slice_start == i:out += slice_openingout += str(arr[i]) + (slice_ending if slice_end == i else "") + ", ";out = out[:-2];out += "]";return outdef main():window_sizes = [3, 2];input_arrays = [[2, 1, 5, 1, 3, 2], [2, 3, 4, 1, 5]];for i in range(len(window_sizes)):print("Window size: " + str(window_sizes[i]) + ", input array: " + str(input_arrays[i]))print("Maximum sum of a subarray of size K = " + str(window_sizes[i]) + ": " + str(max_sub_array_of_size_k(window_sizes[i], input_arrays[i])))print("-------------------------------------------------------------------------------------------------------\n")main()
The code on lines 6 and 12 does not affect program execution. It simply allows us to detect when a new maximum sum has been identified, so that we may indicate this in the output statement on line 17.
How may we modify this code to develop the feature for the computer player for Fizzle?