leetcode 1444. number of ways of cutting a pizza
Problem: number of ways to cut pizza
Leetcode Solution page: my solution
Problem
The problem asks to find the number of ways to cut the pizza k times such that each piece has at least one apple.
Note
This problem cannot be solved with combinatorics. The top half of horizontal cuts is given to a person. The left half of vertical cuts is given to a person.
Intution
The problem is fit for dynammic programming.
Say you make a vertical cut; the number of ways to cut the pizza k times given you made a vertical cut, is simply the the number of ways to cut the remaining pizza k-1 times.
Also, notice that you can represent the remaining pizza by the top left position.
This leads me to the dp states: dp[c][row][col], where c is the remaining cuts to make, row is the topmost row, and col is the leftmost column.
dp[c][row][col] = number of ways to cut the pizza(row, col) k times such that each piece has at least one apple.
So what are the transitions from state to state?
Say you make the leftmost vertical cut on pizza(row, col). The pizza remaining will be pizza(row, col + 1).
Similarly, the topmost horizontal cut yields pizza(row + 1, col). And, we already made clear that the cuts remaining will be c - 1.
Remember, pizza(0, 0) is the starting pizza.
So is this the recurrence?
dp[c][row][col] = (dp[c - 1][row + 1][col] + dp[c - 1][row + 2][col] + … + dp[c - 1][N - 1][col]) + (dp[c - 1][row][col + 1] + dp[c - 1][row][col + 2] + … + dp[c - 1][row][M - 1])
- N = total number of rows
- M = total number of columns
No! You can’t just make these cuts all willy-nilly.
Look at the first term in the first summation: dp[c - 1][row + 1][col]. The only way you can factor that into the result is if the left half has at least one apple in it. How do you confirm that?
Basically, you can use a 2D prefix sum.
pfx[row][col] = number of apples in pizza(row, col) = pfx[row + 1][col] + pfx[row][col + 1] - pfx[row + 1][col + 1]
That means that dp[c - 1][row + 1][col] is included only if (pfx[row][col] - pfx[row + 1][col]) > 0, i.e. the left half of the vertical cut has at least 1 apple
The base case (c = 0 cuts to make) is quite simple: if pfx[row][col] > 0, then dp[0][row][col] = 1. Otherwise, dp[0][row][col] = 0.
And, the recurrence is:
dp[c][row][col] = ((pfx[row][col] - pfx[row + 1][col] > 0) ? dp[c - 1][row + 1][col] : 0) + … + ((pfx[N - 1][col] - pfx[N - 1][col] > 0) ? dp[c - 1][N - 1][col] : 0) +
((pfx[row][col] - pfx[row][col + 1] > 0) ? dp[c - 1][row][col + 1] : 0) + … + ((pfx[row][M - 1] - pfx[N - 1][M - 1] > 0) ? dp[c - 1][row][M - 1] : 0)
Sure, I could write this way clearer in summation notation.
bottom up dynammic programming
bottom up dynammic programming (optimized memory)
top down dynammic programming