class Solution {
public:
long long minimumCost(vector<int>& nums, vector<int>& cost, int k) {
}
};
3500. 将数组分割为子数组的最小代价
给你两个长度相等的整数数组 nums
和 cost
,和一个整数 k
。
你可以将 nums
分割成多个子数组。第 i
个子数组由元素 nums[l..r]
组成,其代价为:
(nums[0] + nums[1] + ... + nums[r] + k * i) * (cost[l] + cost[l + 1] + ... + cost[r])
。注意,i
表示子数组的顺序:第一个子数组为 1,第二个为 2,依此类推。
返回通过任何有效划分得到的 最小 总代价。
子数组 是一个连续的 非空 元素序列。
示例 1:
输入: nums = [3,1,4], cost = [4,6,6], k = 1
输出: 110
解释:
将nums
分割为子数组 [3, 1]
和 [4]
,得到最小总代价。
[3,1]
的代价是 (3 + 1 + 1 * 1) * (4 + 6) = 50
。[4]
的代价是 (3 + 1 + 4 + 1 * 2) * 6 = 60
。示例 2:
输入: nums = [4,8,5,1,14,2,2,12,1], cost = [7,2,8,4,2,2,1,1,2], k = 7
输出: 985
解释:
将nums
分割为子数组 [4, 8, 5, 1]
,[14, 2, 2]
和 [12, 1]
,得到最小总代价。
[4, 8, 5, 1]
的代价是 (4 + 8 + 5 + 1 + 7 * 1) * (7 + 2 + 8 + 4) = 525
。[14, 2, 2]
的代价是 (4 + 8 + 5 + 1 + 14 + 2 + 2 + 7 * 2) * (2 + 2 + 1) = 250
。[12, 1]
的代价是 (4 + 8 + 5 + 1 + 14 + 2 + 2 + 12 + 1 + 7 * 3) * (1 + 2) = 210
。
提示:
1 <= nums.length <= 1000
cost.length == nums.length
1 <= nums[i], cost[i] <= 1000
1 <= k <= 1000
相似题目
原站题解
cpp 解法, 执行用时: 7 ms, 内存消耗: 47.8 MB, 提交时间: 2025-04-03 10:03:46
struct Vec { long long x, y; Vec operator-(const Vec& b) { return {x - b.x, y - b.y}; } long long det(const Vec& b) { return x * b.y - y * b.x; } long long dot(const Vec& b) { return x * b.x + y * b.y; } }; class Solution { public: long long minimumCost(vector<int>& nums, vector<int>& cost, int k) { int total_cost = reduce(cost.begin(), cost.end()); deque<Vec> q = {{0, 0}}; int sum_num = 0, sum_cost = 0; for (int i = 0; i < nums.size(); i++) { sum_num += nums[i]; sum_cost += cost[i]; Vec p = {-sum_num - k, 1}; while (q.size() > 1 && p.dot(q[0]) >= p.dot(q[1])) { q.pop_front(); } // 一边算 DP 一边构建下凸包 p = {sum_cost, p.dot(q[0]) + 1LL * sum_num * sum_cost + k * total_cost}; while (q.size() > 1 && (q.back() - q[q.size() - 2]).det(p - q.back()) <= 0) { q.pop_back(); } q.push_back(p); } return q.back().y; } // 划分型dp long long minimumCost1(vector<int>& nums, vector<int>& cost, int k) { int n = nums.size(); vector<int> s(n + 1); partial_sum(cost.begin(), cost.end(), s.begin() + 1); // cost 的前缀和 vector<long long> f(n + 1, LLONG_MAX); f[0] = 0; int sum_num = 0; for (int i = 1; i <= n; i++) { // 注意这里 i 从 1 开始,下面不用把 i 加一 sum_num += nums[i - 1]; for (int j = 0; j < i; j++) { f[i] = min(f[i], f[j] + 1LL * sum_num * (s[i] - s[j]) + k * (s[n] - s[j])); } } return f[n]; } };
java 解法, 执行用时: 4 ms, 内存消耗: 44.1 MB, 提交时间: 2025-04-03 10:03:03
class Solution { // 划分型dp public long minimumCost1(int[] nums, int[] cost, int k) { int n = nums.length; int[] s = new int[n + 1]; for (int i = 0; i < n; i++) { s[i + 1] = s[i] + cost[i]; // cost 的前缀和 } long[] f = new long[n + 1]; int sumNum = 0; for (int i = 1; i <= n; i++) { // 注意这里 i 从 1 开始,下面不用把 i 加一 sumNum += nums[i - 1]; f[i] = Long.MAX_VALUE; for (int j = 0; j < i; j++) { f[i] = Math.min(f[i], f[j] + (long) sumNum * (s[i] - s[j]) + k * (s[n] - s[j])); } } return f[n]; } // 凸包 + 双指针 / 双端队列 private record Vec(long x, long y) { Vec sub(Vec b) { return new Vec(x - b.x, y - b.y); } long det(Vec b) { return x * b.y - y * b.x; } long dot(Vec b) { return x * b.x + y * b.y; } } public long minimumCost(int[] nums, int[] cost, int k) { int totalCost = 0; for (int c : cost) { totalCost += c; } List<Vec> q = new ArrayList<>(); q.add(new Vec(0, 0)); int sumNum = 0; int sumCost = 0; int j = 0; for (int i = 0; i < nums.length; i++) { sumNum += nums[i]; sumCost += cost[i]; Vec p = new Vec(-sumNum - k, 1); while (j + 1 < q.size() && p.dot(q.get(j)) >= p.dot(q.get(j + 1))) { j++; } // 一边算 DP 一边构建下凸包 p = new Vec(sumCost, p.dot(q.get(j)) + (long) sumNum * sumCost + k * totalCost); while (q.size() - j > 1 && q.getLast().sub(q.get(q.size() - 2)).det(p.sub(q.getLast())) <= 0) { q.removeLast(); } q.add(p); } return q.getLast().y; } }
golang 解法, 执行用时: 0 ms, 内存消耗: 8 MB, 提交时间: 2025-04-03 09:57:19
// 划分型dp func minimumCost1(nums, cost []int, k int) int64 { n := len(nums) s := make([]int, n+1) for i, c := range cost { s[i+1] = s[i] + c // cost 的前缀和 } f := make([]int, n+1) sumNum := 0 for i, x := range nums { i++ // 这里把 i 加一了,下面不用加一 sumNum += x res := math.MaxInt for j := range i { res = min(res, f[j]+sumNum*(s[i]-s[j])+k*(s[n]-s[j])) } f[i] = res } return int64(f[n]) } type vec struct{ x, y int } func (a vec) sub(b vec) vec { return vec{a.x - b.x, a.y - b.y} } func (a vec) det(b vec) int { return a.x*b.y - a.y*b.x } func (a vec) dot(b vec) int { return a.x*b.x + a.y*b.y } func minimumCost(nums, cost []int, k int) int64 { totalCost := 0 for _, c := range cost { totalCost += c } q := []vec{{}} sumNum, sumCost := 0, 0 for i, x := range nums { sumNum += x sumCost += cost[i] p := vec{-sumNum - k, 1} for len(q) > 1 && p.dot(q[0]) >= p.dot(q[1]) { q = q[1:] } // 一边算 DP 一边构建下凸包 p = vec{sumCost, p.dot(q[0]) + sumNum*sumCost + k*totalCost} for len(q) > 1 && q[len(q)-1].sub(q[len(q)-2]).det(p.sub(q[len(q)-1])) <= 0 { q = q[:len(q)-1] } q = append(q, p) } return int64(q[len(q)-1].y) }
python3 解法, 执行用时: 57 ms, 内存消耗: 17.7 MB, 提交时间: 2025-04-03 09:56:26
class Vec: __slots__ = 'x', 'y' def __init__(self, x: int, y: int): self.x = x self.y = y def __sub__(self, b: "Vec") -> "Vec": return Vec(self.x - b.x, self.y - b.y) def det(self, b: "Vec") -> int: return self.x * b.y - self.y * b.x def dot(self, b: "Vec") -> int: return self.x * b.x + self.y * b.y class Solution: # 划分型dp def minimumCost1(self, nums: List[int], cost: List[int], k: int) -> int: n = len(nums) s = list(accumulate(cost, initial=0)) # cost 的前缀和 f = [0] * (n + 1) for i, sum_num in enumerate(accumulate(nums), 1): # 这里把 i 加一了,下面不用加一 f[i] = min(f[j] + sum_num * (s[i] - s[j]) + k * (s[n] - s[j]) for j in range(i)) return f[n] # 凸包 + 双指针 / 双端队列 def minimumCost(self, nums: List[int], cost: List[int], k: int) -> int: total_cost = sum(cost) q = deque([Vec(0, 0)]) sum_num = sum_cost = 0 for x, c in zip(nums, cost): sum_num += x sum_cost += c p = Vec(-sum_num - k, 1) while len(q) > 1 and p.dot(q[0]) >= p.dot(q[1]): q.popleft() # 一边算 DP 一边构建下凸包 p = Vec(sum_cost, p.dot(q[0]) + sum_num * sum_cost + k * total_cost) while len(q) > 1 and (q[-1] - q[-2]).det(p - q[-1]) <= 0: q.pop() q.append(p) return q[-1].y