跳转至

2355. 你能拿走的最大图书数量 🔒

难度困难

题目描述

给定一个长度为 n 的 下标从 0 开始 的整数数组 books,其中 books[i] 表示书架的第 i 个书架上的书的数量。

你要从书架 l 到 r 的一个 连续 的部分中取书,其中 0 <= l <= r < n。对于 l <= i < r 范围内的每个索引 i,你从书架 i 取书的数量必须 严格小于 你从书架 i + 1 取书的数量。

返回你能从书架上拿走的书的 最大 数量。

 

示例 1:

输入: books = [8,5,2,7,9]
输出: 19
解释:
- 从书架 1 上取 1 本书。
- 从书架 2 上取 2 本书。
- 从书架 3 上取 7 本书
- 从书架 4 上取 9 本书
你已经拿了19本书,所以返回 19。
可以证明 19 本是你所能拿走的书的最大数量。

示例 2:

输入: books = [7,0,3,4,5]
输出: 12
解释:
- 从书架 2 上取 3 本书。
- 从书架 3 上取 4 本书。
- 从书架 4 上取 5 本书。
你已经拿了 12 本书,所以返回 12。
可以证明 12 本是你所能拿走的书的最大数量。

示例 3:

输入: books = [8,2,3,7,3,4,0,1,4,3]
输出: 13
解释:
- 从书架 0 上取 1 本书。
- 从书架 1 上取 2 本书。
- 从书架 2 上取 3 本书。
- 从书架 3 上取 7 本书。
你已经拿了 13 本书,所以返回 13。
可以证明 13 本是你所能拿走的书的最大数量。

 

提示:

  • 1 <= books.length <= 105
  • 0 <= books[i] <= 105

解法

方法一:单调栈 + 动态规划

思考

从连续书架取书,且往左每层至少少一本。枚举右端后若再暴力找断裂点,总复杂度过高。

令 \(\textit{nums}[i]=\textit{books}[i]-i\),断裂发生在左侧第一个更小的 \(\textit{nums}[j]\)。单调栈预处理 \(\textit{left}[i]\),再以 \(dp[i]\) 表示以 \(i\) 为右端的最大取书量:中间一段是等差数列,左侧接上 \(dp[j]\)。

我们定义 \(dp[i]\) 表示以书架 \(i\) 为右端点时能取走的书的最大数量。令 \(\textit{nums}[i] = \textit{books}[i] - i\)。若往左的严格递减在某个 \(j\) 处断开( \(0 \le j < i\)),则 \(\textit{books}[j] < \textit{books}[i] - (i - j)\),也就是 \(\textit{nums}[j] < \textit{nums}[i]\)。用单调栈为每个位置求出左侧第一个严格更小的 \(\textit{nums}\),记为 \(j\);不存在时 \(j = -1\)。

从 \(j + 1\) 到 \(i\) 实际取走的本数是公差为 \(1\) 的等差数列。其长度为 \(\textit{cnt} = \min(\textit{books}[i], i - j)\),末项为 \(\textit{books}[i]\),首项为 \(u = \textit{books}[i] - \textit{cnt} + 1\),和为 \(s = (u + \textit{books}[i]) \times \textit{cnt} / 2\)。于是 \(dp[i] = s\),若 \(j \ne -1\) 再加 \(dp[j]\)。

答案为 \(\max\limits_{i} dp[i]\)。

时间复杂度 \(O(n)\),空间复杂度 \(O(n)\)。其中 \(n\) 为数组长度。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Solution:
    def maximumBooks(self, books: List[int]) -> int:
        nums = [v - i for i, v in enumerate(books)]
        n = len(nums)
        left = [-1] * n
        stk = []
        for i, v in enumerate(nums):
            while stk and nums[stk[-1]] >= v:
                stk.pop()
            if stk:
                left[i] = stk[-1]
            stk.append(i)
        ans = 0
        dp = [0] * n
        dp[0] = books[0]
        for i, v in enumerate(books):
            j = left[i]
            cnt = min(v, i - j)
            u = v - cnt + 1
            s = (u + v) * cnt // 2
            dp[i] = s + (0 if j == -1 else dp[j])
            ans = max(ans, dp[i])
        return ans
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class Solution {
    public long maximumBooks(int[] books) {
        int n = books.length;
        int[] nums = new int[n];
        for (int i = 0; i < n; ++i) {
            nums[i] = books[i] - i;
        }
        int[] left = new int[n];
        Arrays.fill(left, -1);
        Deque<Integer> stk = new ArrayDeque<>();
        for (int i = 0; i < n; ++i) {
            while (!stk.isEmpty() && nums[stk.peek()] >= nums[i]) {
                stk.pop();
            }
            if (!stk.isEmpty()) {
                left[i] = stk.peek();
            }
            stk.push(i);
        }
        long ans = 0;
        long[] dp = new long[n];
        dp[0] = books[0];
        for (int i = 0; i < n; ++i) {
            int j = left[i];
            int v = books[i];
            int cnt = Math.min(v, i - j);
            int u = v - cnt + 1;
            long s = (long) (u + v) * cnt / 2;
            dp[i] = s + (j == -1 ? 0 : dp[j]);
            ans = Math.max(ans, dp[i]);
        }
        return ans;
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
using ll = long long;

class Solution {
public:
    long long maximumBooks(vector<int>& books) {
        int n = books.size();
        vector<int> nums(n);
        for (int i = 0; i < n; ++i) nums[i] = books[i] - i;
        vector<int> left(n, -1);
        stack<int> stk;
        for (int i = 0; i < n; ++i) {
            while (!stk.empty() && nums[stk.top()] >= nums[i]) stk.pop();
            if (!stk.empty()) left[i] = stk.top();
            stk.push(i);
        }
        vector<ll> dp(n);
        dp[0] = books[0];
        ll ans = 0;
        for (int i = 0; i < n; ++i) {
            int v = books[i];
            int j = left[i];
            int cnt = min(v, i - j);
            int u = v - cnt + 1;
            ll s = 1ll * (u + v) * cnt / 2;
            dp[i] = s + (j == -1 ? 0 : dp[j]);
            ans = max(ans, dp[i]);
        }
        return ans;
    }
};
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
func maximumBooks(books []int) int64 {
    n := len(books)
    nums := make([]int, n)
    left := make([]int, n)
    for i, v := range books {
        nums[i] = v - i
        left[i] = -1
    }
    stk := []int{}
    for i, v := range nums {
        for len(stk) > 0 && nums[stk[len(stk)-1]] >= v {
            stk = stk[:len(stk)-1]
        }
        if len(stk) > 0 {
            left[i] = stk[len(stk)-1]
        }
        stk = append(stk, i)
    }
    dp := make([]int, n)
    dp[0] = books[0]
    ans := 0
    for i, v := range books {
        j := left[i]
        cnt := min(v, i-j)
        u := v - cnt + 1
        s := (u + v) * cnt / 2
        dp[i] = s
        if j != -1 {
            dp[i] += dp[j]
        }
        ans = max(ans, dp[i])
    }
    return int64(ans)
}

评论