前端算法第一八四弹-K 次调整数组大小浪费的最小总空间

199 阅读1分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第17天,点击查看活动详情

你正在设计一个动态数组。给你一个下标从 0 开始的整数数组 nums ,其中 nums[i]i 时刻数组中的元素数目。除此以外,你还有一个整数 k ,表示你可以 调整 数组大小的 最多 次数(每次都可以调整成 任意 大小)。

t 时刻数组的大小 sizet 必须大于等于 nums[t] ,因为数组需要有足够的空间容纳所有元素。t 时刻 浪费的空间 为 sizet - nums[t] ,总 浪费空间为满足 0 <= t < nums.length 的每一个时刻 t 浪费的空间 之和 。

在调整数组大小不超过 k 次的前提下,请你返回 最小总浪费空间 。

**注意:**数组最开始时可以为 任意大小 ,且 不计入 调整大小的操作次数。

示例 1:

输入:nums = [10,20], k = 0
输出:10
解释:size = [20,20].
我们可以让数组初始大小为 20 。
总浪费空间为 (20 - 10) + (20 - 20) = 10 。

示例 2:

输入:nums = [10,20,30], k = 1
输出:10
解释:size = [20,20,30].
我们可以让数组初始大小为 20 ,然后时刻 2 调整大小为 30 。
总浪费空间为 (20 - 10) + (20 - 20) + (30 - 30) = 10

示例 3:

输入:nums = [10,20,15,30,20], k = 2
输出:15
解释:size = [10,20,20,30,30].
我们可以让数组初始大小为 10 ,时刻 1 调整大小为 20 ,时刻 3 调整大小为 30 。
总浪费空间为 (10 - 10) + (20 - 20) + (20 - 15) + (30 - 30) + (30 - 20) = 15

动态规划 + 预处理

f[i][j]f[i][j] 表示将 nums[0..i]\textit{nums}[0..i] 分成 jj 段的最小总权值。在进行状态转移时,我们可以枚举最后的一段,那么就有状态转移方程:

图片.png

其中 [i]g[i0][i][i]g[i_0][i] 表示nums[i0..i]\textit{nums}[i_0..i] 这一段的最大值乘以这一段的长度再减去这一段的元素和」。在进行动态规划之前,我们可以使用二重循环预处理出所有的 gg 值。

最终的答案即为f[n1][k+1]f[n-1][k+1]

/**
 * @param {number[]} nums
 * @param {number} k
 * @return {number}
 */
var minSpaceWastedKResizing = function(nums, k) {
    // dp[i][j]: [0, i]区间分成j份,最小的浪费空间。
    let dp = new Array(nums.length).fill(0).map(i => new Array(k+2).fill(0));
    let g = new Array(nums.length).fill(0).map(i => new Array(nums.length).fill(0)); // 保存[i,j]区间的空位数
    for (let i=0; i<nums.length; i++) {
        let max = -Infinity;
        let sum = 0;
        for (let j=i; j<nums.length; j++) {
            sum += nums[j];
            max = Math.max(max, nums[j]);
            g[i][j] = (j - i + 1) * max - sum; 
        }
    }
    for (let i=0; i<dp.length; i++) {
        for (let j=1; j<dp[0].length; j++) {
            let min = Infinity;
            for (let t=i; t>=0; t--) {
                let cur;
                if (t-1 < 0) cur = j-1 === 0 ? 0 : Infinity;
                else cur = j-1 === 0 ? Infinity : dp[t-1][j-1];
                min = Math.min(cur + g[t][i], min);
            }
            dp[i][j] = min;
        }
    }
    return dp[nums.length-1][k+1];
};