Dynamic Programming - Largest arrangement of bookcases - algorithm

I'm trying to solve a problem, so I'm not looking for code, but for similar algorithms so I can solve it myself.
I am given n bookcases each with a size amount of books inside. I am to move SOME of these bookcases to a new room as follows:
The first bookcase will always be moved;
I will keep the order of the bookcases in the new room (I can't change positions in the new room, once I selected bookcase 6, I can't select any of the book from 0 to 5);
Bookcase i cannot be placed next to either of the bookcases i-1 or i+1 (ex: I can't place ?-4-5-?/?-5-6-?/?-4-5-6-?);
Which configuration of bookcases will offer me the largest amount of books?
I understand that this is solved using a dynamic programming algorithm, but I am not sure which one. I initially thought it would be similar to the knapsack problem, but I don't have a limit of books so it's clearly different (at least I think it is).
Any suggestions are greatly appreciated!

Make an array int M[n], and set M[0] = b[0] because the first bookcase is always moved. Then proceed as follows:
For each element b[i], where i > 0, set M[i] = b[i]
walk back through elements of M at indexes j ranging from 0 to i-2, inclusive; start at i-2 because you cannot take the bookcase that precedes b[i]
Set M[i] to the max of current M[i] and M[j] + b[i]. The meaning of this expression is "I take b[i] and attach it to the series of bookcases ending at j"
Once the loop is over, walk through M[], and find the highest element. This is your answer.
To print the sequence of bookcase indexes start at the position of max element of M[] (say, p) and print p
Now look back through M for a position k < p such that M[k] = M[p] - b[p]. There will be at least one such element because of the way the array M[] is constructed.
Print k, set p=k, and continue until you get to the beginning of the array.

Related

Why is my solution for finding the maximum product of index difference and minimum index value of a pair working?

The question states - For an array, find the maximum value of (j-i)*min(a[j], a[i]), where i and j are distinct indices of the array in the range {0,1, .., n-1} where n is the array length.
I went with an ill-thought-out solution where I have 2 pointers at both ends of the array and reject the one with a smaller array value. But it is still working. How?
i, j = 0, n-1
ansSoFar = 0
while i <= j:
ansSoFar = max(ansSoFar, (j-i)*min(A[i], A[j]))
if A[i] > A[j]:
j -= 1
else:
i += 1
return ansSoFar
Suppose A[0] is the smaller out of A[0] and A[n-1].
In this case, this is the best pairing we could possibly get with A[0] because if we change the other half of the pair we will definitely reduce (j-i), and may also reduce the value of A[j].
So we know that the best answer is either the current value (including A[0] and A[n-1]), or the best we can get out of elements 1..n-1.
Similarly, if A[n-1] is the smaller, the best answer is either the current value or the best we can get out of elements 0..n-2.
The given code is therefore correct, because it evaluates the current value with the extremes, and then reduces the problem by chopping off the head or the tail as appropriate.

Find minimum number of steps to collect target number of coins

Given a list of n houses, each house has a certain number of coins in it. And a target value t. We have to find the minimum number of steps required to reach the target.
The person can choose to start at any house and then go right or left and collect coins in that direction until it reaches the target value. But the person cannot
change the direction.
Example: 5 1 2 3 4 These are supposed the coin values in 5 houses and the target is 13 then the minimum number of steps required is 5 because we have to select all the coins.
My Thoughts:
One way will be for each index i calculate the steps required in left or right direction to reach the target and then take the minimum of all these 2*n values.
Could there be a better way ?
First, let's simplify and canonize the problem.
Observation 1: The "choose direction" capability is redundant, if you choose to go from house j to house i, you can also go from i to j to have the same value, so it is sufficient to look at one direction only.
Observation 2: Now that we can look at the problem as going from left to right (observation 1), it is clear that we are looking for a subarray whose value exceeds k.
This means that we can canonize the problem:
Given an array with non negative values a, find minimal subarray
with values summing k or more.
There are various ways to solve this, one simple solution using a sorted map (balanced tree for example) is to go from left to right, summing values, and looking for the last element seen whose value was sum - k.
Pseudo code:
solve(array, k):
min_houses = inf
sum = 0
map = new TreeMap()
map.insert(0, -1) // this solves issue where first element is sufficient on its own.
for i from 0 to array.len():
sum = sum + array[i]
candidate = map.FindClosestLowerOrEqual(sum - k)
if candidate == null: // no matching sum, yet
continue
min_houses = min(min_houses, i - candidate)
map.insert(sum, i)
return min_houses
This solution runs in O(nlogn), as each map insertion takes O(logn), and there are n+1 of those.
An optimization, running in O(n), can be done if we take advantage of "non negative" trait of the array. This means, as we go on in the array - the candidate chosen (in the map seek) is always increasing.
We can utilize it to have two pointers running concurrently, and finding best matches, instead of searching from scratch in the index as we did before.
solve(array, k):
left = 0
sum = 0
min_houses = infinity
for right from 0 to len(array):
sum = sum + array[right]
while (left < right && sum >= k):
min_houses = min(min_houses, right - left)
sum = sum - array[left]
left = left + 1
return min_houses
This runs in O(n), as each index is increased at most n times, and every operation is O(1).

Weird step in counting sort from Intro to Algorithms

This seems to be a very common book (Cormen, Leiserson, Rivest, Stein) so hopefully someone will be able to assist. In chapter 8, the algorithm for counting sort is given. It makes sense where you have the input array A and you find the range from 0 to k for the size that array C will be. C[i] is then made to contain the number of elements in A equal to i. For example:
A: [2,5,3,0,2,3,0,3]
C: [2,0,2,3,0,1]
But after this they make it so that C[i] contains the number of elements less than or equal to i. For example:
C: [2,2,4,7,7,8]
Why is this necessary? Couldn't you just iterate through the original C and have a sorted array from that? You know the exact count of each number so you could just go in order putting the right amount of each number in B and have a sorted array. Does transforming C from the first form to the second form somehow make it stable?
I suppose you are proposing to do the following with the intermediate C (using index 1 arrays):
i = 1
for k = 1 to len(C)
for j = 1 to C[i]
B[i] = k
i = i + 1
This seems to be reasonable, and has the same asymptotic running time. However, consider the case where the items whose keys you are sorting on are not just single integers, but have some other data attached to them. The counting algorithm makes the sort stable; relative orders of items with same keys are preserved (see the Wikipedia article). You lose the ability to sort general data if you just assign the output from the indices of C. Hence why the sort assigns elements via B[C[A[j]]] <- A[j].
For others who are curious, this is the completion of the original algorithm:
# C[i] now contains the number of elements equal to i.
for i = 1 to k
C[i] <- C[i] + C[i-1]
# C[i] now contains the number of elements less than or equal to i.
for j = length[A] downto 1
B[C[A[j]]] <- A[j]
C[A[j]] <- C[A[j]] - 1
To explain the decrement in the last part, I cite the book, which also explains the stability of the sort:
Because the elements might not be distinct, we decrement C[A[j]] each time we place a value A[j] into the B array. Decrementing C[A[j]] causes the next input element with a value equal to A[j], if one exists, to go to the position immediately before A[j] in the output array.
Also, if we did that, I guess we wouldn't be able to call it COUNTING-SORT anymore because it wouldn't be counting the number of items less than any particular item in the input array (as they define it). :)

Generate list of real values, which sum up to fixed value and satisfy some constraints

I need to generate n random real values P[0], P[1], ..., P[n-1] which satisfy the following constraints:
Pmin[0] <= P[0] <= Pmax[0]
Pmin[1] <= P[1] <= Pmax[1]
...
Pmin[n-1] <= P[n-1] <= Pmax[n-1]
P[0] + P[1] + ... + P[n-1] = S
Any idea how to do this efficiently?
In general, it is not possible to solve this problem if choosing elements uniformly at random from the given ranges.
Example 1: Say that Pmin[i] = 0 and Pmax[i] = 1. Say that n = 10 and S = 100. Then there is no solution, since the greatest possible sum is 10.
Example 2: Say that Pmin[i] = 0 and Pmax[i] = 1. Say that n = 10 and S = 10. Then there is exactly one solution: choose P[i] = 1.
It is possible to write an algorithm such that the resulting sequence is chosen uniformly at random from the set of possible solutions; this is quite different from saying that the P[i] are uniformly distributed between Pmin[i] and Pmax[i].
The basic idea is to, at each stage, further restrict your range, as follows:
The beginning of the range ought to be the larger of the following two quantities: Pmin[i], or S - Smax[i] - P, where Smax[i] is the sum Pmax[i+1] + ... + Pmax[n] and P is the sum P[0] + ... + P[i]. This guarantees that you're picking a number large enough to eventually work.
The end of the range ought to be the smaller of the following two quantities:
Pmax[i], or S - Smin[i] - P, where Smin[i] is the sum Pmin[i+1] + ... + Pmin[n] and P is as before. This guarantees that you're picking a number small enough to eventually work.
If you are able to obey those rules when picking each P[i], there's a solution, and you will find one at random. Otherwise, there is not a solution.
Note that to actually make this select solutions at random, it's probably best to shuffle the indices, perform this algorithm, and then rearrange the sequence so that it's in the proper order. You can shuffle in O(n), do this algorithm (recommend dynamic programming here, since you can build solutions bottom-up) and then spit out the sequence by "unshuffling" the resulting sequence.
For every i, assign P[i] := Pmin[i]
Compute the sum
If sum>S, then stop (it's impossible)
For every i:
If P[i]+S-sum <= Pmax[i]
P[i] = P[i]+S-sum
Stop (it's done :-)
sum = sum+Pmax[i]-P[i]
P[i] = Pmax[i]
Go for next i
Stop (it's impossible)
Ooops, sorry, you said random... that's not so trivial. Let me think about it...
Run the previous algorithm to have a starting point. Now compute the total margin above and below. The margin above is the sum of individual margins Pmax[i]-P[i] for every i. The margin below is the sum of individual margins P[i]-Pmin[i] for every i.
Traverse all the elements but one in a random order, visiting each one of them exactly once. For every one of them:
Update the margin above and the margin below subtracting from them the contribution of the current element.
Establish a min and max for the current value taking into account that:
They must be in the interval [Pmin[i], Pmax[i]] AND
These min and max are near enough to P[i], so that changing other elements later can compensate changing P[i] to this min or max (that's what the margins above and below indicate)
Change P[i] to a random value in the calculated interval [min, max] and update the sum and the margins (I'm not 100% sure of how the margins should be updated here...)
Then adjust the remaining element to fit the sum S.
Regarding the traversal in random order, see the Knuth shuffles.

Algorithm for finding all combinations of k elements from stack of n "randomly"

I have a problem resembling the one described here:
Algorithm to return all combinations of k elements from n
I am looking for something similar that covers all possible combinations of k from n. However, I need a subset to vary a lot from the one drawn previously. For example, if I were to draw a subset of 3 elements from a set of 8, the following algorithm wouldn't be useful to me since every subset is very similar to the one previously drawn:
11100000,
11010000,
10110000,
01110000,
...
I am looking for an algorithm thats picks the subsets in a more "random" looking fashion, ie. where the majority of elements in one subset is not reused in the next:
11100000,
00010011,
00101100,
...
Does anyone know of such an algorithm?
I hope my question made sence and that someone can help me out =)
Kind regards,
Christian
How about first generating all possible combinations of k from n, and then rearranging them with help of a random function.
If you have the result in a vector, loop through the vector: for each element let it change place with the element at a random position.
This of course becomes slow for large k and n.
This is not really random, but depending on your needs it might suit you.
Calculate the number of possible combinations. Let's name them N.
Calculate a large number which is coprime to N. Let's name it P.
Order the combinations and give them numbers from 1 to N. Let's name them C1 to CN
Iterate for output combinations. The first one will be VP mod N, the second one will be C2*P mod N, the third one C3*P mod N, etc. In essence, Outputi = Ci*P mod N. Mod is meant as the modulus operator.
If P is picked carefully, you will get seemingly random combinations. Values close to 1 or to N will produce values that differ little. Better pick values close to, say N/4 or N/5. You can also randomize the generation of P for every iteration that you need.
As a follow-up to my comment on this answer, here is some code that allows one to determine the composition of a subset from its "index", in colex order.
Shamelessly stolen from my own assignments.
//////////////////////////////////////
// NChooseK
//
// computes n!
// --------
// k!(n-k)!
//
// using Pascal's identity
// i.e. (n,k) = (n-1,k-1) + (n-1,k)
//
// easily optimizable by memoization
long long NChooseK(int n, int k)
{
if(k >= 0 && k <= n && n >= 1)
{
if( k > n / 2)
k = n - k;
if(k == 0 || n == 0)
return 1;
else
return NChooseK(n-1, k-1) + NChooseK(n-1, k);
}
else
return 0;
}
///////////////////////////////////////////////////////////////////////
// SubsetColexUnrank
// The unranking works by finding each element
// in turn, beginning with the biggest, leftmost one.
// We just have to find, for each element, how many subsets there are
// before the one beginning with the elements we have already found.
//
// It stores its results (indices of the elements present in the subset) into T, in ascending order.
void SubsetColexUnrank(long long r, int * T, int subsetSize)
{
assert( subsetSize >= 1 );
// For each element in the k-subset to be found
for(int i = subsetSize; i >= 1; i--)
{
// T[i] cannot be less than i
int x = i;
// Find the smallest element such that, of all the k-subsets that contain it,
// none has a rank that exceeds r.
while( NChooseK(x, i) <= r )
x++;
// update T with the newly found element
T[i] = x;
// if the subset we have to find is not the first one containing this element
if(r > 0)
{
// finding the next element of our k-subset
// is like finding the first one of the same subset
// divided by {T[i]}
r -= NChooseK(x - 1, i);
}
}
}
Random-in, random-out.
The colex order is such that its unranking function does not need the size of the set from which to pick the elements to work; the number of elements is assumed to be NChooseK(size of the set, size of the subset).
How about randomly choosing the k elements. ie choose the pth where p is random between 1 and n, then reorder what's left and choose the qth where q is between 1 and n-1 etc?
or maybe i misunderstood. do you still want all possibilities? in that case you can always generate them first and then choose random entries from your list
By "random looking" I think you mean lexicographically distant.. does this apply to combination i vs. i-1, or i vs. all previous combinations?
If so, here are some suggestions:
since most of the combinators yield ordered output, there are two options:
design or find a generator which somehow yields non-ordered output
enumerate and store enough/all combinations in a tie'd array file/db
if you decide to go with door #2, then you can just access randomly ordered combinations by random integers between 1 and the # of combinations
Just as a final check, compare the current and previous combination using a measure of difference/distance between combinations, e.g. for an unsigned Bit::Vector in Perl:
$vec1->Lexicompare($vec2) >= $MIN_LEX_DIST
You might take another look behind door #1, since even for moderate values of n and k you can get a big array:
EDIT:
Just saw your comment to AnnK... maybe the lexicompare might still help you skip similar combinations?
Depending on what you are trying to do, you could do something like playing cards. Keep two lists: Source is your source (unused) list; and Used the second is the "already-picked" list. As you randomly pick k items from Source, you move them to your Used list.
If there are k items left in Source when you need to pick again, you pick them all and swap the lists. If there are fewer than k items, you pick j items from Used and add them to Source to make k items in Source, then pick them all and swap the lists.
This is kind of like picking k cards from a deck. You discard them to the used pile. Once you reach the end or need more cards, you shuffle the old ones back into play.
This is just to make sure each set is definitely different from the previous subsets.
Also, this will not really guarantee that all possible subsets are picked before old ones start being repeated.
The good is that you don't need to worry about pre-calculating all the subsets, and your memory requirements are linear with your data (2 n-sized lists).

Resources