Facebook Pixel

3219. Minimum Cost for Cutting Cake II


Problem Description

There is a cake with dimensions m x n that needs to be divided into 1 x 1 pieces. You're provided with the integers m, n, and two arrays:

  • horizontalCut of length m - 1, where horizontalCut[i] represents the cost to cut along the horizontal line i.
  • verticalCut of length n - 1, where verticalCut[j] represents the cost to cut along the vertical line j.

In one operation, you can choose any piece of cake that is not yet a 1 x 1 square and perform one of the following cuts:

  1. Cut along a horizontal line i at a cost of horizontalCut[i].
  2. Cut along a vertical line j at a cost of verticalCut[j].

Each cut divides a piece of cake into two smaller ones.

The cost of a cut is determined solely by the initial cost of the line and remains constant. The task is to find the minimum total cost to divide the cake into 1 x 1 pieces.

Intuition

To minimize the total cost of cutting the cake into 1 x 1 pieces, we need a strategy that prioritizes making cuts with higher costs as earlier as possible. This leads us to a greedy approach.

  1. Sorting by Cost: Start by sorting horizontalCut and verticalCut in descending order. This ensures we evaluate the more expensive cuts first.

  2. Two-Pointer Technique: Use two pointers, one for each array (i for horizontalCut and j for verticalCut).

    • Begin by considering the highest-cost available cut between the two arrays.
    • Always apply the cut that has the higher cost between the current horizontal and vertical cuts.
  3. Cost Calculation: When a horizontal cut is made, multiply its cost by the current number of vertical sections (v). When a vertical cut is made, multiply its cost by the current number of horizontal sections (h).

  4. Iterative Update: Each time a horizontal cut is made, increase the number of horizontal sections (h) by 1. Similarly, increase the vertical sections (v) by 1 for each vertical cut.

  5. Completion: Continue this process until both pointers have traversed their respective arrays, ensuring all necessary cuts have been considered.

This strategy leverages the fact that more expensive cuts should be executed when they impact the maximum number of resulting pieces, thus effectively minimizing total expense.

Learn more about Greedy and Sorting patterns.

Solution Approach

To solve the problem of minimizing the cost to cut the cake into 1 x 1 pieces, we deploy a greedy strategy combined with a two-pointer technique. Here's a breakdown of the solution approach:

  1. Sorting:

    • Start by sorting horizontalCut and verticalCut in descending order. This is essential as it allows us to handle the highest cost cuts first, which is crucial in a greedy strategy seeking to minimize overall costs.
  2. Initialization:

    • Initialize two pointers i and j to zero. These pointers will track our position in the horizontalCut and verticalCut arrays, respectively.
    • Set h (count of horizontal sections) and v (count of vertical sections) to 1, as initially the whole cake is a single piece.
  3. Iterative Cutting:

    • Use a loop to continue making cuts until either i >= m - 1 or j >= n - 1. This ensures both dimensions are fully processed.
    • In each iteration, decide between making the next horizontal or vertical cut. If the value at horizontalCut[i] is greater than verticalCut[j], perform the horizontal cut:
      • Add horizontalCut[i] * v to the total cost (ans).
      • Increment h by 1 since an additional horizontal section is created.
      • Move to the next horizontal cut by incrementing i.
    • Else, perform the vertical cut:
      • Add verticalCut[j] * h to the total cost (ans).
      • Increment v by 1 as an additional vertical section is affected.
      • Move to the next vertical cut by incrementing j.
  4. Final Calculation:

    • Once both pointers reach the end of their respective arrays, the loop ends. The accumulated cost in ans is the minimum total cost required to cut the entire cake into 1 x 1 pieces.

This approach ensures that the most impactful decisions are made first by considering the descending order of costs, achieving an optimal solution efficiently. The use of two pointers and sorting simplifies the process, making the implementation both intuitive and effective.

Ready to land your dream job?

Unlock your dream job with a 2-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through a small example to illustrate the solution approach. Consider a cake with dimensions 3 x 3, with the following cutting costs:

  • horizontalCut = [2, 3]
  • verticalCut = [1, 4]

Step-by-step breakdown:

  1. Sorting:

    • Sort both arrays in descending order: horizontalCut = [3, 2], verticalCut = [4, 1].
  2. Initialization:

    • Initialize two pointers: i = 0 and j = 0.
    • Set initial sections: h = 1 (horizontal), v = 1 (vertical).
    • Total cost, ans = 0.
  3. Iterative Cutting:

    • Iteration 1: Compare horizontalCut[i] (3) with verticalCut[j] (4). Since 4 > 3, perform a vertical cut.

      • Add verticalCut[0] * h = 4 * 1 = 4 to ans. (Total cost: ans = 4)
      • Increment v to 2 (2 vertical sections).
      • Increment j to 1.
    • Iteration 2: Now, compare horizontalCut[i] (3) with verticalCut[j] (1). Since 3 > 1, perform a horizontal cut.

      • Add horizontalCut[0] * v = 3 * 2 = 6 to ans. (Total cost: ans = 10)
      • Increment h to 2 (2 horizontal sections).
      • Increment i to 1.
    • Iteration 3: Compare horizontalCut[i] (2) with verticalCut[j] (1). Since 2 > 1, perform a horizontal cut.

      • Add horizontalCut[1] * v = 2 * 2 = 4 to ans. (Total cost: ans = 14)
      • Increment h to 3 (3 horizontal sections).
      • Increment i to 2.
  4. Completion:

    • Now, i = 2, which equals m - 1. All horizontal lines are processed.
    • Only a vertical cut remains with j = 1.
    • Add verticalCut[1] * h = 1 * 3 = 3 to ans. (Final total cost: ans = 17)

The minimum cost to cut the entire cake into 1 x 1 pieces is 17.

Solution Implementation

1from typing import List
2
3class Solution:
4    def minimumCost(
5        self, m: int, n: int, horizontalCut: List[int], verticalCut: List[int]
6    ) -> int:
7        # Sort horizontal and vertical cuts in descending order
8        horizontalCut.sort(reverse=True)
9        verticalCut.sort(reverse=True)
10
11        # Initialize variables to track total cost and index for cuts
12        total_cost = 0
13        i = 0
14        j = 0
15        horizontal_segments = 1
16        vertical_segments = 1
17
18        # Process cuts until all are exhausted
19        while i < m - 1 or j < n - 1:
20            if j == n - 1 or (i < m - 1 and horizontalCut[i] > verticalCut[j]):
21                # Use horizontal cut if all vertical cuts are used or
22                # current horizontal cut is greater than current vertical cut
23                total_cost += horizontalCut[i] * vertical_segments
24                horizontal_segments += 1
25                i += 1
26            else:
27                # Use vertical cut otherwise
28                total_cost += verticalCut[j] * horizontal_segments
29                vertical_segments += 1
30                j += 1
31
32        return total_cost
33
1import java.util.Arrays;
2
3class Solution {
4    public long minimumCost(int m, int n, int[] horizontalCuts, int[] verticalCuts) {
5        // Sort the horizontal and vertical cuts in ascending order
6        Arrays.sort(horizontalCuts);
7        Arrays.sort(verticalCuts);
8      
9        long totalCost = 0; // Initialize the total cost
10        int i = m - 2; // Initialize the index for horizontalCuts, starting just before last element
11        int j = n - 2; // Initialize the index for verticalCuts, starting just before last element
12        int horizontalMultiplier = 1; // Keep track of the segments created by current horizontal cuts
13        int verticalMultiplier = 1; // Keep track of the segments created by current vertical cuts
14      
15        // Process cuts from largest to smallest
16        while (i >= 0 || j >= 0) {
17            // If there are no more vertical cuts to process or the current horizontal cut is larger
18            // than the current vertical cut, process a horizontal cut
19            if (j < 0 || (i >= 0 && horizontalCuts[i] > verticalCuts[j])) {
20                totalCost += (long) horizontalCuts[i--] * verticalMultiplier;
21                horizontalMultiplier++;
22            } else {
23                // Otherwise, process a vertical cut
24                totalCost += (long) verticalCuts[j--] * horizontalMultiplier;
25                verticalMultiplier++;
26            }
27        }
28      
29        return totalCost; // Return the calculated minimum cost
30    }
31}
32
1class Solution {
2public:
3    long long minimumCost(int m, int n, vector<int>& horizontalCut, vector<int>& verticalCut) {
4        // Sort horizontal and vertical cuts in descending order
5        sort(horizontalCut.rbegin(), horizontalCut.rend());
6        sort(verticalCut.rbegin(), verticalCut.rend());
7
8        long long totalCost = 0;  // Variable to store the total minimum cost
9        int i = 0, j = 0;         // Pointers to iterate through horizontal and vertical cuts
10        int horizontalPieces = 1; // Initial number of horizontal pieces
11        int verticalPieces = 1;   // Initial number of vertical pieces
12
13        // Traverse the cuts while there are still cuts left in either direction
14        while (i < m - 1 || j < n - 1) {
15            if (j == n - 1 || (i < m - 1 && horizontalCut[i] > verticalCut[j])) {
16                // Add cost if the next horizontal cut has greater cost or there are no more vertical cuts
17                totalCost += 1LL * horizontalCut[i++] * verticalPieces;
18                horizontalPieces++; // Increase the number of horizontal pieces
19            } else {
20                // Add cost if the next vertical cut has greater cost
21                totalCost += 1LL * verticalCut[j++] * horizontalPieces;
22                verticalPieces++; // Increase the number of vertical pieces
23            }
24        }
25        return totalCost;
26    }
27};
28
1// Function to calculate the minimum cost of cutting a piece of material into smaller sections
2function minimumCost(m: number, n: number, horizontalCut: number[], verticalCut: number[]): number {
3    // Sort cuts in descending order to prioritize larger cuts first
4    horizontalCut.sort((a, b) => b - a);
5    verticalCut.sort((a, b) => b - a);
6
7    let totalCost = 0; // Initialize total cost
8    let [i, j] = [0, 0]; // Initialize indices for horizontal and vertical cuts
9    let [horizontalPieces, verticalPieces] = [1, 1]; // Initialize piece counts
10
11    // Continue until all cuts are processed
12    while (i < m - 1 || j < n - 1) {
13        // Check if vertical cuts are exhausted or horizontal cut is larger
14        if (j === n - 1 || (i < m - 1 && horizontalCut[i] > verticalCut[j])) {
15            // Add the cost of horizontal cut times the number of vertical pieces
16            totalCost += horizontalCut[i] * verticalPieces;
17            horizontalPieces++; // Increase the number of horizontal pieces
18            i++; // Move to the next horizontal cut
19        } else {
20            // Add the cost of vertical cut times the number of horizontal pieces
21            totalCost += verticalCut[j] * horizontalPieces;
22            verticalPieces++; // Increase the number of vertical pieces
23            j++; // Move to the next vertical cut
24        }
25    }
26  
27    return totalCost; // Return the computed total cost
28}
29

Time and Space Complexity

The time complexity is O(m * log m + n * log n), which arises because we sort both horizontalCut and verticalCut. Sorting each array takes O(m * log m) and O(n * log n) time, respectively.

The space complexity is O(log m + log n). This space is primarily used by the sorting process due to the recursive nature of the sorting algorithm, typically requiring additional space related to the recursion depth, which is O(log m) for the horizontal cuts and O(log n) for the vertical cuts.

Learn more about how to find time and space complexity quickly.


Discover Your Strengths and Weaknesses: Take Our 2-Minute Quiz to Tailor Your Study Plan:
Question 1 out of 10

Which of these pictures shows the visit order of a depth-first search?


Recommended Readings

Want a Structured Path to Master System Design Too? Donโ€™t Miss This!


Load More