📜  门| GATE CS 2021 |设置 2 |问题 16(1)

📅  最后修改于: 2023-12-03 15:12:37.789000             🧑  作者: Mango

门 | GATE CS 2021 |设置 2 |问题 16

本题是2021年计算机科学毕业资格考试(GATE CS 2021)第二部分的第16个问题。本题对编程语言的理解及对算法的实现有一定的要求。

问题描述

给定一个包含n个正整数的数组,将其划分为m个非空连续子数组。定义每个子数组的值为其中的最小值。要求最大化这些子数组的值之和。

例如,对于数组 arr = [1, 2, 6, 5, 4, 3],将其划分为 4 个子数组,使得每个子数组中的元素都是连续的。其中的最小值为 [1], [2], [6], [5, 4, 3],最大化这些子数组的值之和为 1+2+6+5=14。

请实现一个函数 int max_sum(int *a, int n, int m) ,其中 a 是一个指向整型数组的指针,n 是数组的长度,m 是需要划分的子数组数量。

示例

输入:

arr = [1, 2, 6, 5, 4, 3], m = 4

输出:

14

解题思路

本问题与单调栈有关。我们用一个栈来存储数组元素的下标。对于前缀 [0, i],我们先用单调栈来求出当前元素 arr[i] 左边的第一个小于其值的下标 prev[i],记为 -1 代表不存在小于其值的元素。这样的话,对于右端点 i,左端点只能在 [prev[i]+1, i] 区间内选择。这时我们需要用动态规划来维护答案。

dp[i][j] 表示将数组前 i 个元素划分为 j 段的最大子数组值之和,则有:

dp[i][j] = max(dp[k][j-1] + min{i, i-1, ..., k+1}*j) for k = prev[i]+1, ..., i-1

简单解释一下上述式子的含义:对于前 i 个数,划分为 j 段的最大子数组和,即为前 k 个数划分为 j-1 段的最大子数组和加上一个长度为 i-k、每个元素值为 min{i, i-1, ..., k+} 的 子数组。其中 min{i, i-1, ..., k+} 可以在预处理时用单调栈求解。

最终的答案就是 dp[n][m]

复杂度分析

由于单调栈只需要遍历一次数组,时间复杂度为 O(N);由于动态规划中 j 的取值范围为 1 到 m,共需求解 m 次,因此时间复杂度为 O(Nm)。

空间复杂度为 O(Nm),与动态规划数组有关。

代码实现

下面是C++语言实现的代码片段,实现方式为单调栈+动态规划:

int max_sum(int *arr, int n, int m) {
    vector<int> left(n), right(n);
    stack<int> stk;
    for (int i = 0; i < n; ++i) {
        while (!stk.empty() && arr[i] <= arr[stk.top()]) stk.pop();
        left[i] = stk.empty() ? -1 : stk.top();
        stk.push(i);
    }
    stk = stack<int>();
    for (int i = n-1; i >= 0; --i) {
        while (!stk.empty() && arr[i] <= arr[stk.top()]) stk.pop();
        right[i] = stk.empty() ? n : stk.top();
        stk.push(i);
    }
    vector<vector<int>> dp(n+1, vector<int>(m+1, 0));
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= m; ++j) {
            if (j == 1) {
                dp[i][j] = arr[left[i-1]+1] * i;
            } else {
                for (int k = left[i-1]+1; k < i; ++k) {
                    dp[i][j] = max(dp[i][j], dp[k][j-1] + (i-k)*arr[i-1]);
                }
            }
        }
    }
    return dp[n][m];
}

需要注意到,本题的四个选项中没有答案与本题求解方法相符的,因此在实际实现时需要判断是否选项A、B、C、D 中有能对应解法的选项。