https://leetcode.cn/problems/minimum-time-to-make-array-sum-at-most-x/description/
代码
1 | class Solution { |
解释
逐行解释代码的功能:
1 | int minimumTime(vector<int>& nums1, vector<int>& nums2, int x) { |
这是 minimumTime 函数的声明,它接收两个整数向量 nums1 和 nums2,以及一个整数 x 作为参数。
1 | int n = nums1.size(); |
这行代码初始化了一个整数 n,它是 nums1(和 nums2,因为它们长度相等)的长度。
1 | vector<vector<int>> dp(n + 1, vector<int>(n + 1, 0)); |
这里声明了一个二维向量 dp,大小为 (n+1) x (n+1),所有元素初始化为 0。dp 用于动态规划,其中 dp[j][i] 将存储考虑到第 j 个元素和 i 次操作时的最佳结果。
1 | vector<pair<int, int>> nums(n); |
创建一个 pair 类型的向量 nums,长度为 n。每个元素都是一个包含来自 nums2 和 nums1 的值的对。
1 | for (int i = 0; i < n; i++) { |
这个循环填充 nums 向量,每个元素都是一个由 nums2[i] 和 nums1[i] 组成的对,这个对可能用于后续的排序和决策。
1 | sort(nums.begin(), nums.end()); |
对 nums 向量进行排序,默认情况下是按照第一个元素(也就是 nums2 的值)进行升序排序。
1 | for (int j = 1; j <= n; j++) { |
这个循环通过 j 来迭代 nums 向量的每个元素。
1 | int b = nums[j - 1].first, a = nums[j - 1].second; |
提取 nums 中的第 j-1 个元素对,将 nums2 的值赋给 b,将 nums1 的值赋给 a。
1 | for (int i = j; i > 0; i--) { |
这个内部循环倒序迭代从 j 到 1 的每个值,用于更新 dp 数组。
1 | dp[j][i] = max(dp[j - 1][i], dp[j - 1][i - 1] + i * b + a); |
更新 dp[j][i] 的值。这里它比较两个选项:不做操作(dp[j - 1][i])和做操作(dp[j - 1][i - 1] + i * b + a),选择它们之间的最大值。
1 | } |
这里闭合了内部 for 循环和外部 for 循环。
1 | int s1 = accumulate(nums1.begin(), nums1.end(), 0); |
计算 nums1 的元素总和。
1 | int s2 = accumulate(nums2.begin(), nums2.end(), 0); |
计算 nums2 的元素总和。
1 | for (int i = 0; i <= n; i++) { |
这个循环用于寻找最少的操作次数,使得 nums1 的总和加上每秒 nums2 产生的增量后不超过 x。
1 | if (s2 * i + s1 - dp[n][i] <= x) { |
检查在 i 次操作后,是否可以将 nums1 的总和减少到 x 或以下。这个条件利用了 dp 数组的结果来确定。
1 | return i; |
如果条件满足,则返回操作次数 `
状态转移方程
1 | dp[j][i] = max(dp[j - 1][i], dp[j - 1][i - 1] + i * b + a); |
这个动态规划(DP)的转移方程是这段代码中最关键的部分,它是决定 dp 数组如何更新的核心。
在这个上下文中,dp[j][i] 表示考虑前 j 个元素,并且进行了 i 次操作时 nums1 的元素总和的最大可能增量。这里的 “操作” 特指的是在每一秒钟结束时,选择一个 nums1 中的元素,并使其增加 nums2 中对应位置的值。
让我们分解这个方程:
dp[j - 1][i]:
这表示如果在当前秒不进行任何操作,nums1的总和将与前一秒j-1时在i次操作下的总和相同。dp[j - 1][i - 1] + i * b + a:
这是一个比较复杂的表达式,它表示如果在当前秒你选择进行操作,你能得到的nums1总和的最大增量。dp[j - 1][i - 1]:这是前一秒在做了i-1次操作的情况下的最大总和增量。i * b:因为每进行一次操作,nums2[j-1](在代码中是b)就会被加到nums1对应的元素上i次(因为已经经过了i秒),所以这里乘以i。a:这是nums1[j-1]在没有任何操作时本应该增加的值,在进行操作后这个增量依然会发生。
然后,max(dp[j - 1][i], dp[j - 1][i - 1] + i * b + a) 这部分比较了两种情况(操作或不操作)并选择了能够得到更大总和的那个选项。
因此,这个转移方程在每一步都在决定:给定当前我们正在考虑的元素(j 个),如果进行了 i 次操作,是继续保持当前的总和(不操作),还是通过增加对应 nums2 的值来尝试增加 nums1 的总和(操作),以便在不超过 x 的条件下达到最小的搬运时间。
这个决策过程在所有元素和所有可能的操作次数上累积起来,最终 dp[n][i] 会告诉我们在进行 i 次操作后能得到的 nums1 的最大总和增量。