Simpler recurrence of Dynamic Programming Rod-cutting - algorithm

In CLRS, the recurrence solution to the rod cutting problem is:
rn = max (pn, r1+rn-1,...,rn-1+r1)
This recurrence is pretty clear and I understood it.
However, I am facing difficulty in understanding the simpler version of this recurrence provided in the book, which is:
rn = max1<=i<=n(pi + rn-i) , where pi is the cost of piece of length i.
I don't understand, how this recurrence is similar to the first recurrence. To me the second recurrence may miss the optimal solution as we are not considering the optimal cost of first cut (we are simply taking it's normal price).
Shouldn't we always consider the optimize cost of the both sides like the first equation?

Here is the reasoning.
An optimal cut must include a piece of some length i. That piece will be sold for price pi. You will then cut the rest of the rod into other pieces, and can do no better than to cut it optimally.
Therefore we just need to find ONE of the pieces in the cut. Recursion will take care of figuring out the rest.
And that is exactly what the simpler recurrence does.

Though this reply is late. I anyway thought of posting it because I got confused along the same lines.
I think the confusion arises when we think of both formulas to be replacement of the other.
Though they count the same phenomena, it is done in two different ways:
Let, B(i) = optimal price for cutting a rod of length i units and p(i) = price of a rod of length i units.
Formula 1: B(i) = max(1<=k<=floor(i/2)) {B(k) + B(i-k)} and P(i)
Formula 2: B(i) = max(1<=k<=i) {p(k) + B(i-k)})
Consider a rod of length 4, it can be cut in the following ways :
1) uncut of length 4
2) 3, 1
3) 2, 2
4) 2, 1, 1
5) 1, 3
6) 1, 2, 1
7) 1, 1, 2
8) 1, 1, 1, 1
According to Formula 1:
option 1 corresponds to P(4)
option 2,5,6,7,8 corresponds to B(1) + B(3)
option 3,4,6,7,8 corresponds to B(2) + B(2)
According to Formula 2:
option 1 corresponds to P(4)
option 2 corresponds to P(3) + B(1)
option 3,4 corresponds to P(2) + B(2)
option 5,6,7,8 corresponds to P(1) + B(3)
So to conclude, 1 and 2 are counting the optimal solution but in different ways, where 2 is more compact and makes lesser recursive calls compared to 1.

In addition to the explanations given by the others, I would like to point out that the second recursion actually eliminates the common rod cutting sub-problems, such as the rutting a rod of size n=4 at either position i=1 or i=3, i.e., cutting off a one-unit piece from either end.

Related

Total number of ways in which the bricks can be arranged on the wall?

Suppose we have a wall of n*3 size, and bricks of size 1*3, 2*3, and 3*3, the bricks can be put horizontally and vertically, what is the total number of ways to arrange the bricks to fill the wall? What is the recurrence relation of this problem?
I think it is T(n) = T(n-1)+ 2T(n-2)+ 7T(n-3), because for T(n-2) we have 1x3+1x3 or 2x3 so 2T(n-2). For three, 1x3+1x3+1x3, 1x3+2x3 or 2x3+1x3 and same for horizontal, plus 3x3 so we have 7dp(n-3), is this correct?
Thank you!
This is almost right, but it over-counts several terms. For example, a solution S for T(n-2) can have two vertical 1-bricks added after it to become a solution for T(n). If you add one 1-brick after S, it's a solution for T(n-1), so the arrangement S + two 1-bricks is being counted in your T(n-2) and T(n-1) terms.
Instead, think about how a solution S for T(n) ends on the right. You can show that the (n-1) x 3 initial segment of S is valid for T(n-1) if and only if the final block of S is a vertical 1-block.
Otherwise, when is the (n-2) x 3 initial segment of S the longest valid initial segment of S? Exactly when S ends with a vertical 2-block (if it ended with two vertical 1-blocks, the longest valid initial segment has length n-1, which we've already counted).
The final case is n-3: figure out how many configurations of the last 3 x 3 space are possible such that the longest valid initial segment of S has length n-3. As a hint: the answer, call it 'c', is smaller than 7, which, as you showed, is the count of all configurations of a 3 x 3 space. These give you the coefficients for the recursion, T(n) = T(n-1) + T(n-2) + c*T(n-3), with appropriate base cases for n = 1, 2 and 3.

Time complexity of listing all paths down stairs?

I am unable to determine the time complexity of a backtracking solution for the climbing stairs problem which states
You are climbing a stair case. It takes n steps to reach to the top.
Each time you can either climb 1 or 2 steps. In how many distinct ways can you climb to the top?
Note: Given n will be a positive integer.
Input: 2
Output: 2
Explanation: There are two ways to climb to the
top.
1 step + 1 step
2 steps
My algorithm:
input = [1, 2]
output = set()
n = 4
def helper(temp):
if sum(temp) == n:
output.add(tuple(temp))
elif sum(temp) > n:
return
else:
for i in input:
helper(temp + [i])
helper([])
print(output)
Output for n = 4:
{(1, 2, 1), (2, 1, 1), (1, 1, 2), (2, 2), (1, 1, 1, 1)}
This function's runtime is the unusual Θ(n · φn), where φ is the golden ratio, (1 + √5) / 2.
To see why this is, let's talk about how to analyze the code that you've written. Imagine the recursion tree for this code. (That's the tree with one node for each recursive call made). Notice that each recursive call branches - there's one call to a subproblem of size n - 1 and one subcall to a problem of size n - 2. In any tree where each internal node is branching, the number of total nodes is at most twice the number of leaves. And in your case, there's one leaf for each solution found, plus some additional leaves for when the recursion overshoots the value of n. (For now, we'll ignore that second group, but we'll talk about why that is later on.) This means that the total number of recursive calls is (with the previous caveat addressed later) at most twice the number of paths down stairs.
So how many solutions are there to this problem? Turns out, the number of solutions for a staircase of height n is exactly equal to the nth Fibonacci number, and the nth Fibonacci number happens to be Θ(φn). So that means that there are a total of Θ(φn) total recursive calls made.
So how much work do those recursive calls take? We can conservatively upper-bound each recursive call's work at O(n), since in the worst case summing up the list adds up 1 + 1 + 1 + ... + 1 n times. But we can also lower-bound the work done at the leaves, where the work is greatest, at Ω(n) because in the best case we add up 2 + 2 + ... + 2 a total of n / 2 times.
Overall, we have Θ(φn) calls, of which the bottom ones do Θ(n) work each, for a total of Θ(n · φn) work.
There's one last detail to address - what about the recursive calls that "overshoot" and add up to something bigger than n? Turns out, there's also O(φn) of these as well. One way to see this is that the number of ways of overshooting to hit n + 1 is at most the number of solutions to listing all paths of size n + 1, and there's O(φn) of these. So adding these back in doesn't change anything.
Hope this helps!

How to solve Twisty Movement from Codeforces?

I've read the editorial but it's very short and claims something I don't understand why it's true. Why is it equivalent to finding longest subsequence of 1*2*1*2*?. Can someone explain the solution step by step and justify the claims at every step? http://codeforces.com/contest/934/problem/C
Here is the 'solution' from the editorial, but as I said it's very short and I don't understand it. Hope someone can guide me to the solution step by step justifying the claims along the way, not like in the solution here. Thanks.
Since 1 ≤ ai ≤ 2, it's equivalent to find a longest subsequence like
1 * 2 * 1 * 2 * . By an easy dynamic programming we can find it in
O(n) or O(n2) time. You can see O(n2) solution in the model
solution below. Here we introduce an O(n) approach: Since the
subsequence can be split into 4 parts (11...22...11...22...) , we
can set dp[i][j](i = 1...n, j = 0..3) be the longest subsequence of
a[1...i] with first j parts.
I also think that the cited explanation is not super clear. Here is another take.
You can collapse an original array
1 1 2 2 2 1 1 2 2 1
into a weighted array
2 3 2 2 1
^ ^ ^ ^ ^
1 2 1 2 1
where numbers at the top represent lengths of contiguous strips of repeated values in the original array.
We can convince ourselves that
The optimal flip does not "break up" any contiguous sequences.
The optimal flip starts and ends with different values (i.e. starts with 1 and ends with 2, or starts with 2 and ends with 1).
Hence, the weighted array contains enough information to solve the problem. We want to flip a contiguous slice of the weighted array s.t. the sum of weights associated with some contiguous monotonic sequence is maximized.
Specifically, we want to perform the flip in such a way that some contiguous monotonic sequence 112, 122, 211 or 221 has maximum weight.
One way to do this with dynamic programming is by creating 4 auxiliary arrays.
A[i] : maximal weight of any 1 to the right of i.
B[i] : maximal weight of any 1 to the left of i.
C[i] : maximal weight of any 2 to the right of i.
D[i] : maximal weight of any 2 to the left of i.
Let's assume that if any of A,B,C,D is accessed out of bounds, the returned value is 0.
We initialize x = 0 and do one pass through the array Arr = [1, 2, 1, 2, 1] with weights W = [2, 3, 2, 2, 1]. At each index i, we have 2 cases:
Arr[i:i+2] == 1 2. In this case we set
x = max(x, W[i] + W[i+1] + C[i+1], W[i] + W[i+1] + B[i-1]).
Arr[i:i+2] == 2 1. In this case we set
x = max(x, W[i] + W[i+1] + A[i+1], W[i] + W[i+1] + D[i-1]).
The resulting x is our answer. This is an O(N) solution.

Merge sort - recursion tree

So I've been studying sorting algorithms.
I am stuck on finding the complexity of merge sort.
Can someone please explain to me how h=1+lg(n)
If you keep dividing n by 2, you'll eventually get to 1.
Namely, it takes log2(n) divisions by 2 to make this happen, by definition of the logarithm.
Every time we divide by 2, we add a new level to the recursion tree.
Add that to the root level (which didn't require any divisions), and we have log2(n) + 1 levels total.
Here's a cooler proof. Notice that, rearranging, we have T(2n) - 2 T(n) = 2 c n.
If n = 2k, then we have T(2k + 1) - 2 T(2k) = 2 c 2k.
Let's simplify the mess. Let's define U(k) = T(2k) / (2 c).
Then we have U(k + 1) - 2 U(k) = 2k, or, if we define U'(k) = U(k + 1) - U(k):
U'(k) - U(k) = 2k
k is discrete here, but we can let it be continuous, and if we do, then U' is the derivative of U.
At that point the solution is obvious: if you've ever taken derivatives, then you know that if the difference of a function and its derivative is exponential, then the function itself has to be exponential (since only in that case will the derivative be some multiple of itself).
At that point you know U(k) is exponential, so you can just plug in an exponential for the unknown coefficients in the exponential, and plug it back in to solve for T.

Solving addition chain problems using dynamic programming

You are given a positive integer A. The goal is to construct the shortest possible sequence of integers ending with A, using the following rules:
The first element of the sequence is 1
Each of the successive elements is the sum of any two preceding elements (adding a single element to itself is also permissible)
Each element is larger than all the preceding elements; that is, the sequence is increasing.
For example, for A = 42, a possible solution is:
[1, 2, 3, 6, 12, 24, 30, 42]
Another possible solution for A = 42 is:
[1, 2, 4, 5, 8, 16, 21, 42]
After reading the problem statement, the first thing that came to my mind is dynamic programming (DP), hence I expressed it as a search problem and tried to write a recursive solution.
The search space up to A = 8 is:
1
|
2
/ \
/ \
3 4
/|\ /|\
/ | \ 5 6 8
/ | \
4 5 6
/| |\
5 6 7 8
We can see that 4 occurs in two places, but in both cases the children of 4 are different. In one case the prior sequence is [1, 2, 4]. In the other case the prior sequence is [1, 2, 3, 4]. Therefore, we cannot say that we have overlapping sub-problems. Is there any way to apply DP to the above problem? Or I am wrong in judging that it can be solve using DP?
This is an addition chain...
http://en.wikipedia.org/wiki/Addition_chain
There is no known algorithm which can calculate a
minimal addition chain for a given number with any guarantees of
reasonable timing or small memory usage. However, several techniques
to calculate relatively short chains exist. One very well known
technique to calculate relatively short addition chains is the binary
method, similar to exponentiation by squaring. Other well-known
methods are the factor method and window method.
See also: New Methods For Generating Short Addition Chains in IEICE Transactions on Fundamentals of Electronics, Communications and Computer Sciences.
There is a pure dynamic programming solution to this problem. But as all DP solutions, the complexity for both memory and time is N squared. So it can be a space hog. But here is the crux of the DP solution just for DP lovers:
Generalization:
Instead of finding the min length of addition chain for N viz. l(N), we are to find the min length of addition chain that contains both i and j with i &leq; j and j being max in the chain. Call this length A(i, j). Obviously, we have
l(N) = A(1, N) = A(N,N)
A(1, N) = A(2, N) if N &geq; 2
A(1, N) = 1+min (A(i, j) for 1 &leq; i &leq; j < N and i+j=N
Advance Calculation
To calculate any A(m, n) by using smaller A(i, j), a typical DP step, we apply a number of heuristics.
H1: maintain an S(i+j) for the min part of the (3) along the way. Then
A(1, n) = A(2, n) = A(n, n) = S(n)+1
For other m, we reduce n by introducing at most one more new element to the chain, and with such a new element, we just need one more step to conclude A(m, n). Possibilities are
H2: if n is even, we attempt to introduce n/2 to the chain
H3: attempt to introduce n-m to the chain
H4: attempt to introduce n-1 to the chain
H5: attempt to introduce n-2 to the chain (when n>2)
So A(m, n) to take the min of A of m and the reduced n based on H2-H5, plus 1.
Example, A(52, 100) = 1+min(A(50, 52), A(48, 52), A(52, 99), A(52, 98)) by applying H2-H5 respectively.
Due to rule #2,
Each of the successive elements is the sum of any two preceding elements
The algorithm does rely on duplicate subproblems, so Dynamic Programming would be suitable for solving it.

Resources