Rod Cutting - Dynamic Programming - algorithm

Problem Statement
The rod-cutting problem is the following. Given a rod of length n inches and a table of prices Pi for i = 1, 2, 3,....n, determine the maximum revenue Rn obtain- able by cutting up the rod and selling the pieces. Note that if the price Pn for a rod of length n is large enough, an optimal solution may require no cutting at all.
Consider the case whenn=4. Figure shows all the ways to cut up a rod of 4 inches in length, including the way with no cuts at all. We see that cutting a 4-inch rod into two 2-inch pieces produces revenue P2+P2=5+5=10, which is optimal.
The below code is a bottom-up approach of building the solution for rod-cutting.
for (i = 1; i<=n; i++)
{
int q = INT_MIN;
for (j = 0; j < i; j++)
q= max(q, p[j] + r[i-j-1]);
r[i] = q;
}
return val[n];
Why do we need an auxiliary array r[n+1]? Couldn't the problem be solved only by using just array p? Is it used because we cannot access p[-1] when we are cutting of rod length n and 0?
Why are we using q = max(q, p[j] + r[i-j-1]) when p is not updated to new values?

You should use two different arrays r and p, because their meaning is completely different. The value p[i] tells you, how much a complete (not cut) board of length i+1 costs. The value r[i] tells you, how much profit you can make with a board of length i+1 (complete or cut into pieces). These values are not the same. For instance in you example you have p[3] = 9, but r[3] = 10, because you can cut the board of length 4 into two smaller pieces of length 2. Keeping the two different meanings in separate arrays is most always a good idea. (Except if you have very tight memory restrictions)
Also, in practice you will likely not sell boards of length 100. But you might want to know the optimal profit, that you can make with a board of this size by cutting it. If you only have one array, you would have to enlarge it. Depending one your language choice this also might involve creating a second array and copying the first array. So it would be easier to simply use a second array.
Notice, that it is possible though (if n is smaller than the langth of the array p). A simple solution that uses only one array would be (using one-indexed):
int p[]={0,1,5,8,9,10,17,17,20,24,30};
int n = 4;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= i/2; j++)
p[i] = max(p[i], p[j] + p[i - j]);
}
printf("%d\n", p[n]);

If I understood the question correctly, then it is impossible to remove r from the implementation. Apparently the semantics of r is
r[i] = maximum profit attainabble by cutting a rod of length i
into pieces of the lengths 1,...,n
and it needs to be accessed in the inner loop. The recurrence relation in the inner loop translates to
q = the more profitable choice between not cutting a rod of length j
and cutting a rod of length j (in which case we take p[j] as
profit plus the maximum attainable profit of cutting the remaining
rod, which has length j-i)
which means that the information in r is necessary for the evaluation.

Rod cutting problem without using auxiliary array in the inner loop and iterating it only by half.
#include <stdio.h>
#include <limits.h>
int max(int a,int b)
{
return a>b?a:b;
}
int cut_rod(int p[],int n)
{
int q=0;
int r[n+1]; // Auxiliary array for copying p and appending 0 at index 0
int i,j;
if(n<0)
return 0;
else
{
r[0]=0;
for(i=0;i<n;i++)
r[i+1]=p[i];
for(i=1;i<=n+1;i++)
{
q=INT_MIN;
for(j=0;j<=i/2;j++)
q=max(q,r[j]+r[i-j-1]);
r[i-1]=q;
}
}
return r[n];
}
int main()
{
int p[]={1,5,8,9,10,17,17,20,24,30};
int n=sizeof(p)/sizeof(int);
int val;
val=cut_rod(p,n);
printf("%d",val);
return 0;
}

Related

Dynamic Programming - Rod Cutting Bottom Up Algorithm (CLRS) Solution Incorrect?

For the "rod cutting" problem:
Given a rod of length n inches and an array of prices that contains prices of all pieces of size smaller than n. Determine the maximum value obtainable by cutting up the rod and selling the pieces. [link]
Introduction to Algorithms (CLRS) page 366 gives this pseudocode for a bottom-up (dynamic programming) approach:
1. BOTTOM-UP-CUT-ROD(p, n)
2. let r[0 to n]be a new array .
3. r[0] = 0
4. for j = 1 to n
5. q = -infinity
6. for i = 1 to j
7. q = max(q, p[i] + r[j - i])
8. r[j] = q
9. return r[n]
Now, I'm having trouble understanding the logic behind line 6. Why are they doing max(q, p[i] + r[j - i]) instead of max(q, r[i] + r[j - i])? Since, this is a bottom up approach, we'll compute r[1] first and then r[2], r[3]... so on. This means while computing r[x] we are guaranteed to have r[x - 1].
r[x] denotes the max value we can get for a rod of length x (after cutting it up to maximize profit) whereas p[x] denotes the price of a single piece of rod of length x. Lines 3 - 8 are computing the value r[j] for j = 1 to n and lines 5 - 6 are computing the maximum price we can sell a rod of length j for by considering all the possible cuts. So, how does it ever make sense to use p[i] instead of r[i] in line 6. If trying to find the max price for a rod after we cut it at length = i, shouldn't we add the prices of r[i] and r[j - 1]?
I've used this logic to write a Java code and it seems to give the correct output for a number of test cases I've tried. Am I missing some cases in which where my code produces incorrect / inefficient solutions? Please help me out. Thanks!
class Solution {
private static int cost(int[] prices, int n) {
if (n == 0) {
return 0;
}
int[] maxPrice = new int[n];
for (int i = 0; i < n; i++) {
maxPrice[i] = -1;
}
for (int i = 1; i <= n; i++) {
int q = Integer.MIN_VALUE;
if (i <= prices.length) {
q = prices[i - 1];
}
for (int j = i - 1; j >= (n / 2); j--) {
q = Math.max(q, maxPrice[j - 1] + maxPrice[i - j - 1]);
}
maxPrice[i - 1] = q;
}
return maxPrice[n - 1];
}
public static void main(String[] args) {
int[] prices = {1, 5, 8, 9, 10, 17, 17, 20};
System.out.println(cost(prices, 8));
}
}
They should be equivalent.
The intuition behind the CLRS approach is that they are trying to find the single "last cut", assuming that the last piece of rod has length i and thus has value exactly p[i]. In this formulation, the "last piece" of length i is not cut further, but the remainder of length j-i is.
Your approach considers all splits of the rod into two pieces, where each of the two parts can be cut further. This considers a superset of cases compared to the CLRS approach.
Both approaches are correct and have the same asymptotic complexity. However, I would argue that the CLRS solution is more "canonical" because it more closely matches a common form of DP solution where you only consider the last "thing" (in this case, the last piece of uncut rod).
I guess both of the approach are correct.
before we prove both of them are correct lets define what exactly each approach does
p[i] + r[j - i] will give you the max value you can obtain from a rod of length j and of the piece is of size "i"(cannot divide that piece further)
r[i] + r[j-i] will give you the max value you can obtain from a rod of length i and the first cut was made at length "i"(can devide both the pieces further)
Now consider we have a rod of length X and the solution set will contain piece of length k
and since k is 0 < k < X you will find the max value at p[k] + r[X-k] in the first approach
and in the second approach you can find the same result with r[k] + r[X-k] since we know that r[k] will be >= p[k]
But in you approach you can get the result much faster(half of the time) since you are slicing the rod from both ends
so in you approach you can run the inner loop for half of the length should be good.
But I think in you code there is a bug in inner for loop
it should be j >= (i / 2) instead of j >= (n / 2)

Algorithm for selecting closest pairs using Dynamic Programming

I have been trying to solve this problem my professor has given me but couldn't make a proper solution. The following is the problem
Problem:
A rectangular circuit board has two parallel sides with width W between them. There are m terminals on the upper side of the board and n terminals (n < m) on the lower side. Let U1 < U[2] < … < U[m] be the distances from the left end of the board to the m terminals on the upper side, respectively. Let L1 < L[2] < … < L[n] be the distances from the left end of the board to the n terminals on the lower side, respectively. Now, we need to select n terminals from the m terminals on the upper side to be connected to the n terminals on the lower side by n straight line segments, respectively, such that the total length of the n line segments is minimized. The following figure illustrates the problem for m = 8 and n = 4.
(a) Prove that, in an optimal solution, any two line segments will not intersect.
(b) Design an O(mn) dynamic programming algorithm to solve this minimization problem. You need to define sub-problems, show the inductive formula, initial conditions, and a pseudocode. You can use d(i, j) to denote the distance between U[i] and L[j], 1 ≤ i ≤ m, 1 ≤ j ≤ n. (The calculation of d(i, j) = ) can be omitted.
My Approach:
For the above problem, my approach was first to make a matrix d(i,j) where i are the terminals on the bottom and j are the terminals on the top. d(i,j) has all the distances from any two circuits.Then iterating through each row I will find the smallest distance and mark the respective terminal. But I am not sure this would work if the top circuits are all to the extreme right of the side. So can anyone provide me with a better approach.
I have written a recursive Dynamic Programming solution that uses memoisation, the complexity is O(mn), here at each recursive level we can either choose to join the current point defined in the U[] array with the point defined in the L[] array, or we can move forward without doing so:
#include<iostream>
#define INF 1e9
using namespace std;
int n, m, d[100][100], dp[100][100];
int solve(int idx1, int idx2){
if(idx1 > m){
if(idx2 < n) return INF;
else return 0;
}
if(idx2 > n) return 0;
if(dp[idx1][idx2] != -1) return dp[idx1][idx2];
int v1, v2;
//include current
v1 = solve(idx1 + 1, idx2 + 1) + d[idx1][idx2];
//do not include current
v2 = solve(idx1 + 1, idx2);
return dp[idx1][idx2] = min(v1, v2);
}
int main(){
//enter the the distances
for(int i = 0;i < 100;i++) for(int j = 0;j < 100;j++) dp[i][j] = -1;
cout << solve(1, 1) << endl;
return 0;
}
For the part (a) of your question, let us assume that 2 line segments do intersect, then we cannot have an optimal solution because if we just swapped the 2 end points of the line segments defined by the L[] array then the distance would reduce, hence giving us a better solution.

Find minimum cost to convert array to arithmetic progression

I recently encountered this question in an interview. I couldn't really come up with an algorithm for this.
Given an array of unsorted integers, we have to find the minimum cost in which this array can be converted to an Arithmetic Progression where a cost of 1 unit is incurred if any element is changed in the array. Also, the value of the element ranges between (-inf,inf).
I sort of realised that DP can be used here, but I couldn't solve the equation. There were some constraints on the values, but I don't remember them. I am just looking for high level pseudo code.
EDIT
Here's a correct solution, unfortunately, while simple to understand it's not very efficient at O(n^3).
function costAP(arr) {
if(arr.length < 3) { return 0; }
var minCost = arr.length;
for(var i = 0; i < arr.length - 1; i++) {
for(var j = i + 1; j < arr.length; j++) {
var delta = (arr[j] - arr[i]) / (j - i);
var cost = 0;
for(var k = 0; k < arr.length; k++) {
if(k == i) { continue; }
if((arr[k] + delta * (i - k)) != arr[i]) { cost++; }
}
if(cost < minCost) { minCost = cost; }
}
}
return minCost;
}
Find the relative delta between every distinct pair of indices in the array
Use the relative delta to test the cost of transforming the whole array to AP using that delta
Return the minimum cost
Louis Ricci had the right basic idea of looking for the largest existing arithmetic progression, but assumed that it would have to appear in a single run, when in fact the elements of this progression can appear in any subset of the positions, e.g.:
1 42 3 69 5 1111 2222 8
requires just 4 changes:
42 69 1111 2222
1 3 5 8
To calculate this, notice that every AP has a rightmost element. We can suppose each element i of the input vector to be the rightmost AP position in turn, and for each such i consider all positions j to the left of i, determining the step size implied for each (i, j) combination and, when this is integer (indicating a valid AP), add one to the the number of elements that imply this step size and end at position i -- since all such elements belong to the same AP. The overall maximum is then the longest AP:
struct solution {
int len;
int pos;
int step;
};
solution longestArithProg(vector<int> const& v) {
solution best = { -1, 0, 0 };
for (int i = 1; i < v.size(); ++i) {
unordered_map<int, int> bestForStep;
for (int j = 0; j < i; ++j) {
int step = (v[i] - v[j]) / (i - j);
if (step * (i - j) == v[i] - v[j]) {
// This j gives an integer step size: record that j lies on this AP
int len = ++bestForStep[step];
if (len > best.len) {
best.len = len;
best.pos = i;
best.step = step;
}
}
}
}
++best.len; // We never counted the final element in the AP
return best;
}
The above C++ code uses O(n^2) time and O(n) space, since it loops over every pair of positions i and j, performing a single hash read and write for each. To answer the original problem:
int howManyChangesNeeded(vector<int> const& v) {
return v.size() - longestArithProg(v).len;
}
This problem has a simple geometric interpretation, which shows that it can be solved in O(n^2) time and probably can't be solved any faster than that (reduction from 3SUM). Suppose our array is [1, 2, 10, 3, 5]. We can write that array as a sequence of points
(0,1), (1,2), (2,10), (3,3), (4,5)
in which the x-value is the index of the array item and the y-value is the value of the array item. The question now becomes one of finding a line which passes the maximum possible number of points in that set. The cost of converting the array is the number of points not on a line, which is minimized when the number of points on a line is maximized.
A fairly definitive answer to that question is given in this SO posting: What is the most efficient algorithm to find a straight line that goes through most points?
The idea: for each point P in the set from left to right, find the line passing through that point and a maximum number of points to the right of P. (We don't need to look at points to the left of P because they would have been caught in an earlier iteration).
To find the maximum number of P-collinear points to the right of P, for each such point Q calculate the slope of the line segment PQ. Tally up the different slopes in a hash map. The slope which maps to the maximum number of hits is what you're looking for.
Technical issue: you probably don't want to use floating point arithmetic to calculate the slopes. On the other hand, if you use rational numbers, you potentially have to calculate the greatest common divisor in order to compare fractions by comparing numerator and denominator, which multiplies running time by a factor of log n. Instead, you should check equality of rational numbers a/b and c/d by testing whether ad == bc.
The SO posting referenced above gives a reduction from 3SUM, i.e., this problem is 3SUM-hard which shows that if this problem could be solved substantially faster than O(n^2), then 3SUM could also be solved substantially faster than O(n^2). This is where the condition that the integers are in (-inf,inf) comes in. If it is known that the integers are from a bounded set, the reduction from 3SUM is not definitive.
An interesting further question is whether the idea in the Wikipedia for solving 3SUM in O(n + N log N) time when the integers are in the bounded set (-N,N) can be used to solve the minimum cost to convert an array to an AP problem in time faster than O(n^2).
Given the array a = [a_1, a_2, ..., a_n] of unsorted integers, let diffs = [a_2-a_1, a_3-a_2, ..., a_n-a_(n-1)].
Find the maximum occurring value in diffs and adjust any values in a necessary so that all neighboring values differ by this amount.
Interestingly,even I had the same question in my campus recruitment test today.While doing the test itself,I realised that this logic of altering elements based on most frequent differences between 2 subsequent elements in the array fails in some cases.
Eg-4,5,8,9 .According to the logic of a2-a1,a3-a2 as proposed above,answer shud be 1 which is not the case.
As you suggested DP,I feel it can be on the lines of considering 2 values for each element in array-cost when it is modified as well as when it is not modified and return minimum of the 2.Finally terminate when you reach end of the array.

Sample an index of a maximal number in an array, with a probability of 1/(number of maximal numbers)

This is one of the recent interview question that I faced. Program to return the index of the maximum number in the array [ To Note : the array may or may not contain multiple copies of maximum number ] such that each index ( which contains the maximum numbers ) have the probability of 1/no of max numbers to be returned.
Examples:
[-1 3 2 3 3], each of positions [1,3,4] have the probability 1/3 to be returned (the three 3s)
[ 2 4 6 6 3 1 6 6 ], each of [2,3,6,7] have the probability of 1/4 to be returned (corresponding to the position of the 6s).
First, I gave O(n) time and O(n) space algorithm where I collect the set of max-indexes and then return a random number from the set. But he asked for a O(n) time and O(1) complexity program and then I came up with this.
int find_maxIndex(vector<int> a)
{
max = a[0];
max_index = 0;
count = 0;
for(i = 1 to a.size())
{
if(max < a[i])
{
max = a[i];
count = 0;
}
if(max == a[i])
{
count++;
if(rand < 1/count) //rand = a random number in the range of [0,1]
max_index = i;
}
}
return max_index;
}
I gave him this solution. But my doubt is if this procedure would select one of the indexes of max numbers with equal probability. Hope I am clear.Is there any other method to do this ?
What you have is Reservoir sampling! There is another easy to understand solution, but requires two passes.
int find_maxIndex(vector<int> a){
int count = 1;
int maxElement = a[0];
for(int i = 1; i < a.size(); i++){
if(a[i] == maxElement){
count ++;
} else if(a[i] > maxElement){
count = 1;
maxElement = a[i];
}
}
int occurrence = rand() % count + 1;
int occur = 0;
for(int i = 0; i < a.size(); i++){
if(a[i] == maxElement){
occur++;
if(occur == occurrence) return i;
}
}
}
The algorithm is pretty simple, first find the number of times the max element occurs in the first pass. And choose a random occurrence and return the index of that occurrence. It takes two passes though, but very easy to understand.
Your algorithm works fine, and you can prove it via induction.
That is, assuming it works for any array of size N, prove it works for any array of size N+1.
So, given an array of size N+1, think of it as a sub-array of size N followed a new element at the end. By assumption, your algorithm uniformly selects one of the max elements of the sub-array... And then it behaves as follows:
If the new element is larger than the max of the sub-array, return that element. This is obviously correct.
If the new element is less than the max of the sub-array, return the result of the algorithm on the sub-array. Also obviously correct.
The only slightly tricky part is when the new element equals the max element of the sub-array. In this case, let the number of max elements in the sub-array be k. Then, by hypothesis, your algorithm selected one of them with probability 1/k. By keeping that same element with probability k/(k+1), you make the overall probability of selecting that same element equal 1/k * k /(k+1) == 1/(k+1), as desired. You also select the last element with the same probability, so we are done.
To complete the inductive proof, just verify the algorithm works on an array of size 1. Also, for quality of implementation purposes, fix it not to crash on arrays of size zero :-)
[Update]
Incidentally, this algorithm and its proof are closely related to the Fisher-Yates shuffle (which I always thought was "Knuth's card-shuffling algorithm", but Wikipedia says I am behind the times).
The idea is sound, but the devil is in the details.
First off, what language are you using? It might make a difference. The rand() from C and C++ will return an integer, which isn't likely to be less than 1/count unless it returns 0. Even then, if 1/count is an integer division, that result is always going to be 0.
Also your count is off by 1. It starts as 1 when you get a new max, but you immediately increment it in the next if statement.

Sum of the largest odd divisors of the first n numbers

I've been working on topcoder recently and I stumbled upon this question which I can't quite make understand.
The question is to find F(n) = f(1)+f(2)+....+f(n) for a given "n" such that f(n) is the largest odd divisor for n.
There are many trivial solutions for the answer; however, I found this solution very intriguing.
int compute(n) {
if(n==0) return 0;
long k = (n+1)/2;
return k*k + compute(n/2);
}
However, I don't quite understand how to obtain a recursive relation from a problem statement such as this. Could someone help out?
I believe they are trying to use the following facts:
f(2k+1) = 2k+1, i.e. the largest odd divisor of an odd number is the number itself.
f(2k) = f(k). i.e the largest odd divisor of an even number 2m is same as the largest odd divisor of the number m.
Sum of first k odd numbers is equal to k^2.
Now split {1,2,..., 2m+1} as {1,3,5,7,...} and {2,4,6,...,2m} and try to apply the above facts.
You can use dynamic approach also using auxiliary spaces
int sum=0;
int a[n+1];
for(int i=1;i<=n;i++){
if(i%2!=0)
a[i] = i;
else
a[i] = a[i/2];
}
for(int i=1;i<=n;i++){
sum+=a[i];
}
cout<<sum;
As when number is odd then the number itself will be the greatest odd divisor and a[i] will store it's value and when number is even then the a[number/2] will be stored in a[i] because for even number the greatest odd divisor of number/2 will be the greatest odd divisor of the number.
It can also be solved using three cases when number is odd then add number itself else if number is power of 2 then add 1 else if number is even except power of 2 divide it by 2 till you get odd and add that odd to sum.
I cannot see how that algorithm could possible work for the problem you described. (I'm going to assume that "N" and "n" refer to the same variable).
Given n = 12.
The largest odd divisor is 3 (the others are 1, 2, 4, 6 & 12)
F(12) is therefor f(1) + f(2) + f(3) or 1 + 1 + 3 or 5.
Using this algorithm:
k = (12+1)/2 or 6
and we return 6 * 6 + f(6), or 36 + some number which is not going to be negative 31.
if this were Java, I'd say:
import java.util.*;
int sum_largest_odd_factors (int n){
ArrayList<Integer> array = new ArrayList();//poorly named, I know
array.add(1);
for(int j = 2; j <= n; j++){
array.add(greatestOddFactor(j));
}
int sum = 0;
for(int i = 0; i < array.size(); i++){
sum += array.get(i);
}
return sum;
}
int greatestOddFactor(int n){
int greatestOdd = 1;
for(int i = n-((n%2)+1); i >= 1; i-=2){
//i: starts at n if odd or n-1 if even
if(n%i == 0){
greatestOdd = i;
break;
//stop when reach first odd factor b/c it's the largest
}
}
return greatestOdd;
}
This is admittedly tedious and probably an O(n^2) operation, but will work every time. I'll leave it to you to translate to C++ as Java and J are the only languages I can work with (and even that on a low level). I'm curious as to what ingenious algorithms other people can come up with to make this much quicker.
IF u are looking for sum of all the odd divisors till n..
Sum of the all odd divisors of the first n numbers
...
for(long long int i=1;i<=r;i=i+2)
{
sum1=sum1+i*(r/i);
}
for sum of all divisors in a range l to r
for(long long int i=1;i<=r;i=i+2)
{
sum1=sum1+i*(r/i);
}
for(long long int i=1;i<l;i=i+2)
{
sum2=sum2+i*((l-1)/i);
}
ans=sum1-sum2;;;
THANK YOU!!

Resources