
Problem Statement
In this challenge, you are provided with an array books
representing the number of books on each shelf of a bookshelf, indexed from 0
to n-1
. The task is to select books from a contiguous subset of shelves, denoted by indices l
to r
, such that for each shelf i
in the range l
to r - 1
, the number of books you take from shelf i
must be strictly less than the number taken from shelf i + 1
.
Your goal is to determine the maximum total number of books you can collect under these constraints.
Examples
Example 1
Input:
books = [8,5,2,7,9]
Output:
19
Explanation:
- Take 1 book from shelf 1. - Take 2 books from shelf 2. - Take 7 books from shelf 3. - Take 9 books from shelf 4. Total = 1 + 2 + 7 + 9 = 19.
Example 2
Input:
books = [7,0,3,4,5]
Output:
12
Explanation:
- Take 3 books from shelf 2. - Take 4 books from shelf 3. - Take 5 books from shelf 4. Total = 3 + 4 + 5 = 12.
Example 3
Input:
books = [8,2,3,7,3,4,0,1,4,3]
Output:
13
Explanation:
- Take 1 book from shelf 0. - Take 2 books from shelf 1. - Take 3 books from shelf 2. - Take 7 books from shelf 3. Total = 1 + 2 + 3 + 7 = 13.
Constraints
1 <= books.length <= 10⁵
0 <= books[i] <= 10⁵
Approach and Intuition
The core challenge is to find a contiguous subarray in which we can select books in strictly increasing quantities, such that the selected quantity from each shelf does not exceed the shelf's available books.
To maximize the total number of books taken:
Use a Stack or Monotonic Pattern:
- Iterate backward through
books
, maintaining a stack to simulate increasing sequences from right to left. - Greedily attempt to take as many books as allowed while ensuring the strictly increasing constraint.
- Iterate backward through
Greedy Backward Sweep:
- Let
books[i]
be the current shelf. - Keep track of the maximum number of books you are allowed to take at this position (
limit
). Start with infinity. - At each step, set
take = min(books[i], limit)
. - Accumulate the total and update
limit = take - 1
. - Stop when
limit == 0
.
- Let
Slide Over All Endpoints:
- Try each possible endpoint
r
from right to left and simulate collecting books backward to somel
, computing the total for that range. - Keep track of the maximum total found over all iterations.
- Try each possible endpoint
This strategy ensures we explore all valid ranges efficiently and find the one that yields the highest number of books under the increasing constraint.
Time Complexity
- The algorithm runs in O(n) due to linear traversal and greedy bookkeeping, suitable for the constraint
n <= 10⁵
.
Solutions
- C++
- Java
- Python
class Solution {
public:
long long getMaxBooks(vector<int>& shelf) {
int len = shelf.size();
// Lambda to compute sum of books between two indices inclusive
auto calcSum = [&](int start, int end) {
long long maxCnt = min(shelf[end], end - start + 1);
return (2 * shelf[end] - (maxCnt - 1)) * maxCnt / 2;
};
stack<int> indices;
vector<long long> dp(len);
for (int i = 0; i < len; i++) {
// Maintain the conditions for the stack
while (!indices.empty() && shelf[indices.top()] - indices.top() >= shelf[i] - i) {
indices.pop();
}
// Calculate dp[i] based on previous values
if (indices.empty()) {
dp[i] = calcSum(0, i);
} else {
int prev = indices.top();
dp[i] = dp[prev] + calcSum(prev + 1, i);
}
// Add current index to stack
indices.push(i);
}
// Find the maximum value in dp
return *max_element(dp.begin(), dp.end());
}
};
This solution, implemented in C++, is designed to find the maximum number of books one can take from a series of bookshelves represented by a vector of integers, where each integer specifies the number of books that can be taken from that particular bookshelf. The approach uses dynamic programming to optimize the process, complemented by a stack to help maintain needed indices for state transitions.
- Start by initializing
dp
, a dynamic programming vector to store maximum books that can be acquired up to each index. - Utilize a stack to help keep track of indices that are potentially beneficial for future states. The stack ensures that the properties of the solution are consistent, especially when calculating sums over certain ranges efficiently.
- Use a lambda function
calcSum
to efficiently calculate the maximum books from a specific range of bookshelves. It computes the possible number of books considering the bounds set by the last shelf in the current segment. - As the solution iterates over each bookshelf, it determines the value of
dp
for each location either by calculating a sum from the beginning usingcalcSum
when the stack is empty, or based on a relative calculation derived from the last relevant position stored in the stack. After computing the value fordp[i]
, the current indexi
is pushed onto the stack. - Once the iteration completes, the maximum value in the
dp
vector is found and returned. This value represents the maximum number of books that can be taken.
Overall, this efficient approach combines dynamic programming with clever usage of a stack and lambda expressions to dynamically and optimally solve the problem of selecting the maximum number of books across a series of bookshelves. The algorithm judiciously balances between recalculating sums and using pre-calculated values to ensure optimal performance.
class Solution {
public long maxBooksStack(int[] booksArray) {
int length = booksArray.length;
Stack<Integer> indexStack = new Stack<>();
long[] dpResults = new long[length];
for (int index = 0; index < length; index++) {
while (!indexStack.isEmpty() && booksArray[indexStack.peek()] - indexStack.peek() >= booksArray[index] - index) {
indexStack.pop();
}
if (indexStack.isEmpty()) {
dpResults[index] = sumOfBooks(booksArray, 0, index);
} else {
int previousIndex = indexStack.peek();
dpResults[index] = dpResults[previousIndex] + sumOfBooks(booksArray, previousIndex + 1, index);
}
indexStack.push(index);
}
return Arrays.stream(dpResults).max().getAsLong();
}
private long sumOfBooks(int[] booksArray, int start, int end) {
long count = Math.min(booksArray[end], end - start + 1);
return (2 * booksArray[end] - (count - 1)) * count / 2;
}
}
The solution provided outlines an efficient method to find the maximum number of books one can take under certain conditions, implemented in Java. The problem uses a dynamic programming approach with a stack and the following steps provide an overview of the solution:
Initialize an array
dpResults
to store the results of dynamic programming and aStack<Integer>
namedindexStack
to help determine range boundaries for subproblems efficiently.Using a for loop, iterate through each book count in the array
booksArray
. During each iteration:- While
indexStack
is not empty and the current book's adjusted position is less or equal to the adjusted position at the peek ofindexStack
, pop the stack. - If
indexStack
is empty after the pop operations, compute the sum of books from the start to the current index using a helper methodsumOfBooks
. Otherwise, use the last index fromindexStack
to compute and add the sum of books between this index and current. - Push the current index onto
indexStack
.
- While
After filling up the
dpResults
, use Java'sArrays.stream
to find and return the maximum value.
The helper method sumOfBooks
, calculates the sum based on a mathematical formula considering the position and number of books from a specified range inducing optimization in the repetitive addition tasks by leveraging arithmetic progression principles.
This systematic approach takes into account the peculiarities of each book's position related to its count while ensuring efficiency using dynamic programming and a stack-based mechanism. Remember, the implementation correctly calculates the maximum sum by adjusting the array-based computations and using a stack to maintain relevant indices for subproblems. This results in optimal time complexity suitable for the problem's requirements.
class Solution:
def maxBooksCollected(self, shelves: List[int]) -> int:
shelfCount = len(shelves)
# Customize function to compute sum of books from index left to right
def getSumBooks(left, right):
books = min(shelves[right], right - left + 1)
return (2 * shelves[right] - (books - 1)) * books // 2
indexStack = []
bookDp = [0] * shelfCount
for index in range(shelfCount):
while indexStack and shelves[indexStack[-1]] - indexStack[-1] >= shelves[index] - index:
indexStack.pop()
if not indexStack:
bookDp[index] = getSumBooks(0, index)
else:
lastValid = indexStack[-1]
bookDp[index] = bookDp[lastValid] + getSumBooks(lastValid + 1, index)
indexStack.append(index)
return max(bookDp)
This Python solution focuses on solving the problem of finding the maximum number of books one can take from a series of shelves represented by an array, where each element denotes the maximum height of books that can be taken from that particular shelf.
The function maxBooksCollected
computes the result using dynamic programming and a stack data structure to optimize the process. Here’s a brief overview of the solution implementation:
Dynamic Programming Table Initialization: A list
bookDp
is initialized with zeros to store the maximum number of books that can be taken up to each shelf.Stack for Index Tracking: An empty list
indexStack
is used to keep track of indices for shelves, helping in efficient computation of book numbers using previous result values.Helper Function -
getSumBooks
: This function calculates the maximum books that can be taken between two specified indices. It utilizes the mathematical formula involving the minimum value constraint on the shelves and computes a triangular number to sum up the books.Logic within the Loop:
- The loop iterates through each shelf index.
- It adjusts the
indexStack
by popping indices to maintain correct bounds for the maximum books calculation from the current index. - Depending on whether the stack is empty or not, it calculates the sum of books up to the current shelf using the
getSumBooks
function directly or adds it to the sum from a previously valid index stored inbookDp
. - Current index is then added to the stack for future reference.
Final Result: After processing all shelves, the function returns the maximum value from
bookDp
, which represents the maximum number of books that can be taken from the shelves.
This solution efficiently utilizes dynamic programming alongside a stack to manage and compute the required values dynamically, ensuring optimal performance even with larger inputs. The use of auxiliary space for DP storage and index management helps in maintaining an efficient and comprehensible algorithm flow.
No comments yet.