http://datagenetics.com/blog/july22012/index.html
I am using the mentioned link to understand the eggdrop problem. I have also looked at code online which I understand to decent extent (the recursion is a bit confusing) but I can't seem to understand a few main things about this egg drop problem.
Suppose we have 100 floors
For 2 eggs we say we start at 14 and then go to 14 + (14-1). I understand why we do this to keep the worse case time uniform and all. However, where will we start for three eggs? The formula shows that 3 eggs will have a max of 9 tries in the worst case. Obviously we don't start from 9 because going 9 + ( 9 - 1 ) doesn't give us a consisten 9 trials across 100 so where do we start for 3? Not only that but how do we figure this out?
It seems like for 3 eggs, we run a few trials until the problem degenerates into 2 eggs and x amount of floors. Conceptually this makes sense but I don't understand how to visualize or implement this
I have to find the sequence of tries in a worst case scenario and implement it which is why I want to visualize the tries.
I hope this is clear. It is the first bullet of mine that is my main issue. Please let me know if I'm missing out any info and I'll edit it.
The equation n(n+1)/2 = F can only be used to solve the min worst case number of drops in the case of 2 eggs. Here also happens to be one of the floors you can drop the 1st egg from because of the uniformity you mentioned. You know you can drop from the 14th floor because if it cracks you'll have, at worst, 13 more drops. But if it doesn't crack and you go up 13 floors and it cracks, you will have at worst 12 more drops to go with 2 under your belt already. With this pattern you can see that by dropping the egg from nth floor, then n+(n-1) floor, then n+(n-1)+(n-2) floor your worst case is staying the same at each threshold.
This is what you want to achieve regardless of how many eggs you start with, but finding an optimal floor (n) which makes this condition true (which actually can be expressed as a range as #Amit pointed out) can't be calculated with a closed series like it can for 2 eggs. It is important to note that even in the case of (n+1)n = F, n is just one of many answers for the possible first floor value. We conveniently say it is the answer, perhaps carelessly, because we can prove it to be true using a relatively simple series.
So let's use a more general approach to estimate the minimum worst case number of drops and then see at which floors we know that this can be achieved. Let's say we have function, g(floors, eggs), which returns the minimum worst case egg drops needed for a particular amount of floors. We can say with confidence that if eggs = 1, the worst case scenario is that we will have to drop the egg from every floor to find the threshold, so g(floors, 1) = floors is true for any value of floors. We also know that if we have 1 floor to test it will always only require one drop so g(1, eggs) = 1. Beyond these two cases we know the following:
g(floors, eggs) = Min(n = 1->floors : 1 + max(g(n-1,e-1),g(floors-n, e)))
Why does it work? Well given an amount of total floors, one must go through every single one and see what the worst case is for cracking the egg at each. This is done by comparing worst case if it cracks, or g([current floor]-1, eggs-1), with worst case if it doesn't crack, or g(floors-[current floor], eggs).
The maximum of these two values will be the worst case scenario for that particular floor. If we track a global minimum of each of these maxima, we will find the lowest drops required for the worst case. The floor(s) at which this happens is the optimal floor to drop the egg at. Now let's plug in eggs = 2 in that function to get a better feeling as to why this works, and why we can also represent the min worst case number of drops when starting with 2 eggs as a series as well.
When we have precisely 2 eggs we will always be comparing g([current floor]-1, 1) with g(floors-[current floor], 2). This makes things a bit easier because we know exactly what the worst case is if we crack the egg at current floor:
*worst case drops required* = g(cf-1, 1) + 1 = 1 + (cf-1)
note-here we add the 1 because we have already done 1 drop by the time we can test the remaining floors below.
We also know that the two functions we are comparing at each floor (cf) are monotonically going in two different directions for any fixed number of total floors F. This must be true because:
g(cf+d, 1) > g(cf, 1) for any positive cf and d so this function is increasing as you increase cf.
g(F-(cf+d),2) < g(F-cf,2), thus this function is always decreasing as you increase cf.
The above is important because it makes us realize that the minimum max of these two functions will happen at a floor (let's call it optimal floor, of) where they return values that are closest to one another, one could even dare to say equal to each other! Using this we can approximate that the minimum occurs when:
g(of1-1, 1) ~= of1-1 ~= g(F- of1, 2) ~= 1+(of2-1) ~= 1+ g(F- of1 - of2, 2) ~= 2+(of3-1) ~=.....~= of2 --> the value furthest on the right represents the worst case if the egg doesn't crack at any of the optimal thresholds, then we will have used up of2 number of drops to get to that point, not counting the very first drop.
where of1 is the theoretical floor where the first drop can occur to minimize the worst case and of1+of2 is where the second drop must occur (given failure to crack at of1), all the way up until of1 + of2...+ofn = F. Let's now examine the relationship between of1 and of2:
(of1-1) = 1+(of2-1), so of2 = of1-1
similarly
of3 = of2 -1 = of1 - 2
finally we can say that in general
ofn = of1-(n-1)
We know that if we have gone through all threshold floors except for the last, and none cracked the egg, then we are on our of1th drop.
ofof1 = of1-(of1-1) =1 => this element is the last in our series
The series, of1 + of2...+ofn can be written as of1 + (of1-1) + (of1-2) +....+1 = F which we know can be expressed as (of1)(of1+1)/2 = F. This makes finding both the minimum worst case number of drops and the optimal floor to drop the first egg a simple exercise of plugging in F into this formula.
Okay now let's use the same function when eggs equals three! Well unfortunately it turns out that you hit a wall in the very first step. Remember when we were still comparing g([current floor]-1, eggs-1) with g(floors-[current floor], eggs)? Well let's say eggs = 3 now so you are comparing g(floors-[current floor], 3) and g([current floor]-1, 2).
We cannot reduce any of these functions into a series with a closed form solution as the function
g(F - cf, 3)
requires at least one level of recursion to solve so whatever we reduce it to will always have a term with g function in it. On the other hand if we try to utilize the fact that for any
g(f-1, 2)
there exists an n where (n+1)n/2 = f-1, where n is the minimum worst case number of drops.
If we rearrange (n+1)n/2 = f-1 to n = 1/2(sqrt(8f-7)-1) = g(f-1, 2)
we could potentially try to set g(of1-1, 2) equal to 1+g(of2-1, 2) to find of2 as a function of of1, similarly to how we found of2 expressed in terms of of1 when were starting with 2 eggs. If you recall we put all "optimum" floors for drops, expressed in terms of the optimum floor for the first drop, in a series that happened to have a closed form solution. With 3 eggs we run out of luck as this results in an "ugly" series with no way of solving without recursion. This is unlike in the case where we were starting with 2 eggs because we were able to reduce g(cf-1, 1) + 1 into just 1 + (cf-1). This helped us build the series which did have a closed form solution.
Therefore there is no nifty derivation to use to find the optimal first drop like there is in the case of 2 eggs. Below I wrote a short algo that outputs both min worst case number of drops and optimal floor for first drop (often times it can be more than one but I always return the last). Once you put in a value other than 2 for e you can notice that these will not necessarily equal one another.
var optimumFloorForFirstDrop = 0;
var F = 24;
console.log("Minimum worst case number of drops: " +findBestWorstCase(F,3) + ", optimum floor for first drop: "+ optimumFloorForFirstDrop);
function findBestWorstCase(n, e){
//if we have 0 or 1 floors or one egg then return the number of floor...this is pretty intuitive
if(n < 2 || e == 1) return n;
//we want to go through every floor and keep track of the minimum for a particular n of the max function below
var min = n;
for(var i = 1; i <= n; i++){
var tmpMax = 1 + Math.max(findBestWorstCase(i-1, e-1),findBestWorstCase(n-i, e));
if(tmpMax <= min){
min = tmpMax;
if(n==F) optimumFloorForFirstDrop = i;
}
}
return min;
}
Let's examine a few examples first:
If you have 1 egg, how many throws do you need for a 2 floors building? 3 floors? 4?
Obviously that's simple. You need 2, 3 and 4 throws, respectively, and generally, n throws.
What if you have 2 eggs? How many throws for 2 floors? 3? 4?
Obviously 2 throws for 2 floors... 3 is interesting though. If you throw an egg from the 2nd floor, and it doesn't break, you throw it from the 3rd floor and you know the answer (it either breaks on the 3rd floor, or doesn't at all). If the first egg breaks on the 2nd floor throw, you throw the other egg from the first floor and again you know the answer (it either breaks on the 1st floor and that's the answer, or it doesn't and the answer is the 2nd floor). Ha... so only 2 throws for 3 floors. But that doesn't work for the 4th floor and we need another throw. That extra throw can "buy us" more than just one floor. It will actually get us to the 6th floor (we'll soon see how that works).
It also turns out that having more eggs won't make any difference for a 6 floor building or less.
Suppose we know how many floors we can cover with m-1 eggs and n-1 throws, let's call that h. If we have m eggs and n throws, our optimal strategy would be to through the first egg from the h+1th floor - that's the highest floor we can go for. If the egg breaks, we have enough eggs (m-1) and enough throws (n-1) to find the answer in the remaining h floors. Our next move if the egg doesn't break is to go up enough floors so that we're covered by (m, n-1) and keep doing that till we have no throws left. This way we will achieve the maximum coverage with any combination of eggs and throws.
That explains our optimal strategy, but we haven’t defined how many floors will (m, n) cover. That’s quite simple though: (m-1, n-1) will cover some h floors, the (m, n) throw itself will account for the h+1 floor, and the remaining throws will allow for additional (m, n-1) floors.
The modeling of the problem should be quite clear by now, and we can define a simple recursive function to calculate the maximal covered height:
function maxHeightByEggThrows(eggs, throws) {
if(eggs === 0 || throws === 0)
return 0;
return maxHeightByEggThrows(eggs - 1, throws - 1) + 1 +
maxHeightByEggThrows(eggs, throws - 1);
}
And this works flawlessly, but it’s a poor, ineffective implementation. Let’s try DP:
function maxHeightByEggThrowsDP(eggs, throws) {
let eggThrows = [[]];
for(let i = 0; i < throws; i++) {
// A single egg can cover has many floors has throws are allowed
eggThrows[0].push(i + 1);
}
for(let i = 1; i < eggs; i++) {
// Any number of eggs can only cover 1 floor with a single throw
eggThrows.push([1]);
}
for(let i = 1; i < throws; i++) {
for(let j = 1; j < eggs; j++) {
eggThrows[j][i] = eggThrows[j - 1][i - 1] + eggThrows[j][i - 1] + 1;
}
}
return eggThrows[eggs - 1][throws - 1];
}
This doesn't look as nice, but it's much better for performance reasons, and, we can use such an implementation to return the whole table and display it:
function maxHeightByEggThrowsDP(eggs, throws) {
let eggThrows = [[]];
for (let i = 0; i < throws; i++) {
// A single egg can cover has many floors has throws are allowed
eggThrows[0].push(i + 1);
}
for (let i = 1; i < eggs; i++) {
// Any number of eggs can only cover 1 floor with a single throw
eggThrows.push([1]);
}
for (let i = 1; i < throws; i++) {
for (let j = 1; j < eggs; j++) {
eggThrows[j][i] = eggThrows[j - 1][i - 1] + eggThrows[j][i - 1] + 1;
}
}
return eggThrows;
}
const eggs = 10;
const throws = 15;
let eggThrows = maxHeightByEggThrowsDP(eggs, throws);
// display our data (boilerplate code)
// add a "row header" so we can read the egg count
eggThrows.forEach((row, i) => row.unshift(i + 1));
d3.select('#eggThrows>tbody').selectAll('tr').data(eggThrows).enter().append('tr').selectAll('td').data(d => d).enter().append('td').text(t => t);
d3.select('#throwsHeader').attr('colSpan', throws);
#eggThrows > thead td {
padding: 5px;
background-color: #404040;
color: white;
}
#eggThrows > tbody td {
padding: 5px;
background-color: #C0C0C0;
}
#eggThrows > tbody td:first-child {
background-color: #C0FF30;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<table id="eggThrows"><thead><tr><td>Eggs</td><td id="throwsHeader">Throws</td></tr></thead><tbody></tbody></table>
So we know the maximum height, but we wanted the first throw floor... It turns out there are multiple options. For example, we can now see that in the classic "2 eggs 14 throws" case, we can actually get an answer for 105 floors, not just 100. We also know that 2 eggs and 13 throws covers 91 floors, so we could throw the first egg from the 9th floor and still manage to find a solution within 14 throws.
And now we can answer the question:
The first floor to throw from is not higher than (maxHeightByEggThrows(m-1, n-1) + 1) and not lower than ([building height] - maxHeightByEggThrows(m, n-1))
(for 3 eggs and 100 floors, this is between the 8th and 37th floors)
This problem can be solved with following 3 approaches (that I know) :
Dynamic Programming
Solution using Binary Search Tree
Solution by obtaining the direct mathematical formula for maximum number of floors that can be tested or covered with given number of eggs and given number of drops
Let me first define some symbols that are used in analysis done afterwards :
e = number of eggs
f = number of floors in building
n = number of egg drops
Fmax(e, n) = maximum number of floors that can be tested or covered with e eggs and n drops
The crux for dynamic programming approach lies in following recursive formula for Fmax:
Fmax(e, n) = 1 + Fmax(e-1, n-1) + fmax(e, n-1)
And the crux for obtaining the direct mathematical formula for Fmax lies in following recursive formula for Fmax:
Fmax(e, n) = { ∑Fmax(e-1,i) for i = 1 to n } - Fmax(e-1, n) + n
Alternative solution using Binary Search Tree (BST) is also possible for this problem. In order to facilitate our analysis, let us draw BST with slight modifications as follows:
1. If egg breaks then child node is drawn on left down side
2. If egg does not break then child node is drawn straight down side
If we draw BST with above kind of representation then width of the BST represents the number of eggs.
Any BST with f number of nodes, drawn with above kind of representation and subjected to the constraint width of BST <= e (number of eggs) is a solution but it may not be the optimal solution.
Hence obtaining the optimal solution is equivalent to obtaining the arrangement of nodes in BST with minimum height subjected to the constraint: width of BST <= e
For more details about all the above 3 approaches, check my blog at: 3 approaches for solving generalized egg drop problem
Here is the algorithm which have implemented in swift(This includes concept of all previous algorithm):-
//Here n is number of eggs and k is number of floors
func eggDrop(_ n: Int, _ k: Int) -> Int {
var eggFloor: [[Int]] = Array(repeating:Array(repeating: 0, count: k+1),count: n+1)
if k == 1 {
return k
}
for i in 1...n {
eggFloor[i][1] = 1
eggFloor[i][0] = 0
}
for j in 1...k {
eggFloor[1][j] = j
}
for i in 2...n {
for j in 2...k {
eggFloor[i][j] = Int(INT_MAX)
for x in 1...j {
let attempts = 1 + max(eggFloor[i-1][x-1], eggFloor[i][j-x])
if attempts < eggFloor[i][j] {
eggFloor[i][j] = attempts
}
}
}
}
return eggFloor[n][k]
}
Related
i have an appointment for university which is due today and i start getting nervous. We recently discussed dynamic programming for algorithm optimization and now we shall implement an algorithm ourself which uses dynamic programming.
Task
So we have a simple game for which we shall write an algorithm to find the best possible strategy to get the best possible score (assuming both players play optimized).
We have a row of numbers like 4 7 2 3 (note that according to the task description it is not asured that it always is an equal count of numbers). Now each player turnwise takes a number from the back or the front. When the last number is picked the numbers are summed up for each player and the resulting scores for each player are substracted from each other. The result is then the score for player 1. So an optimal order for the above numbers would be
P1: 3 -> p2: 4 -> p1: 7 -> p2: 2
So p1 would have 3, 7 and p2 would have 4, 2 which results in a final score of (3 + 7) - (4 + 2) = 4 for player 1.
In the first task we should simply implement "an easy recursive way of solving this" where i just used a minimax algorithm which seemed to be fine for the automated test. In the second task however i am stuck since we shall now work with dynamic programming techniques. The only hint i found was that in the task itself a matrix is mentioned.
What i know so far
We had an example of a word converting problem where such a matrix was used it was called Edit distance of two words which means how many changes (Insertions, Deletions, Substitutions) of letters does it take to change one word into another. There the two words where ordered as a table or matrix and for each combination of the word the distance would be calculated.
Example:
W H A T
| D | I
v v
W A N T
editing distance would be 2. And you had a table where each editing distance for each substring was displayed like this:
"" W H A T
1 2 3 4
W 1 0 1 2 3
A 2 1 1 2 3
N 3 2 2 2 3
T 4 3 3 3 2
So for example from WHA to WAN would take 2 edits: insert N and delete H, from WH to WAN would also take 2 edits: substitude H->A and insert N and so on. These values where calculated with an "OPT" function which i think stands for optimization.
I also leanred about bottom-up and top-down recursive schemes but im not quite sure how to attach that to my problem.
What i thought about
As a reminder i use the numbers 4 7 2 3.
i learned from the above that i should try to create a table where each possible result is displayed (like minimax just that it will be saved before). I then created a simple table where i tried to include the possible draws which can be made like this (which i think is my OPT function):
4 7 2 3
------------------
a. 4 | 0 -3 2 1
|
b. 7 | 3 0 5 4
|
c. 2 | -2 -5 0 -1
|
d. 3 | -1 -4 1 0
the left column marks player 1 draws, the upper row marks player 2 draws and each number then stands for numberP1 - numberP2. From this table i can at least read the above mentioned optimal strategy of 3 -> 4 -> 7 -> 2 (-1 + 5) so im sure that the table should contain all possible results, but im not quite sure now how to draw the results from it. I had the idea to start iterating over the rows and pick the one with the highest number in it and mark that as the pick from p1 (but that would be greedy anyways). p2 would then search this row for the lowest number and pick that specific entry which would then be the turn.
Example:
p1 picks row a. 7 | 3 0 5 4 since 5 is the highest value in the table. P2 now picks the 3 from that row because it is the lowest (the 0 is an invalid draw since it is the same number and you cant pick that twice) so the first turn would be 7 -> 4 but then i noticed that this draw is not possible since the 7 is not accessible from the start. So for each turn you have only 4 possibilities: the outer numbers of the table and the ones which are directly after/before them since these would be accessable after drawing. So for the first turn i only have rows a. or d. and from that p1 could pick:
4 which leaves p2 with 7 or 3. Or p1 takes 3 which leaves p2 with 4 or 2
But i dont really know how to draw a conclusion out of that and im really stuck.
So i would really like to know if im on the right way with that or if im overthinking this pretty much. Is this the right way to solve this?
The first thing you should try to write down, when starting a dynamic programming algorithm, is a recurrence relation.
Let's first simplify a very little the problem. We will consider that the number of cards is even, and that we want to design an optimal strategy for the first player to play. Once we have managed to solve this version of the problem, the others (odd number of cards, optimize strategy for second player) follows trivially.
So, first, a recurrence relation. Let X(i, j) be the best possible score that player 1 can expect (when player 2 plays optimally as well), when the cards remaining are from the i^th to the j^th ones. Then, the best score that player 1 can expect when playing the game will be represented by X(1, n).
We have:
X(i, j) = max(Arr[i] + X(i+1, j), X(i, j-1) + Arr[j]) if j-i % 2 == 1, meaning that the best score that player one can expect is the best between taking the card on the left, and taking the card on the right.
In the other case, the other player is playing, so he'll try to minimize:
X(i, j) = min(Arr[i] + X(i+1, j), X(i, j-1) + Arr[j]) if j-i % 2 == 0.
The terminal case is trivial: X(i, i) = Arr[i], meaning that when there is only one card, we just pick it, and that's all.
Now the algorithm without dynamic programming, here we only write the recurrence relation as a recursive algorithm:
function get_value(Arr, i, j) {
if i == j {
return Arr[i]
} else if j - i % 2 == 0 {
return max(
Arr[i] + get_value(i+1, j),
get_value(i, j-1) + Arr[j]
)
} else {
return min(
Arr[i] + get_value(i+1, j),
get_value(i, j-1) + Arr[j]
)
}
}
The problem with this function is that for some given i, j, there will be many redundant calculations of X(i, j). The essence of dynamic programming is to store intermediate results in order to prevent redundant calculations.
Algo with dynamic programming (X is initialized with + inf everywhere.
function get_value(Arr, X, i, j) {
if X[i][j] != +inf {
return X[i][j]
} else if i == j {
result = Arr[i]
} else if j - i % 2 == 0 {
result = max(
Arr[i] + get_value(i+1, j),
get_value(i, j-1) + Arr[j]
)
} else {
result = min(
Arr[i] + get_value(i+1, j),
get_value(i, j-1) + Arr[j]
)
}
X[i][j] = result
return result
}
As you can see the only difference with the algorithm above is that we now use a 2D array X to store intermediate results. The consequence on time complexity is huge, since the first algorithm runs in O(2^n), while the second runs in O(n²).
Dynamic programming problems can generally be solved in 2 ways, top down and bottom up.
Bottom up requires building a data structure from the simplest to the most complex case. This is harder to write, but offers the option of throwing away parts of the data that you know you won't need again. Top down requires writing a recursive function, and then memoizing. So bottom up can be more efficient, top down is usually easier to write.
I will show both. The naive approach can be:
def best_game(numbers):
if 0 == len(numbers):
return 0
else:
score_l = numbers[0] - best_game(numbers[1:])
score_r = numbers[-1] - best_game(numbers[0:-1])
return max(score_l, score_r)
But we're passing a lot of redundant data. So let's reorganize it slightly.
def best_game(numbers):
def _best_game(i, j):
if j <= i:
return 0
else:
score_l = numbers[i] - _best_game(i+1, j)
score_r = numbers[j-1] - _best_game(i, j-1)
return max(score_l, score_r)
return _best_game(0, len(numbers))
And now we can add a caching layer to memoize it:
def best_game(numbers):
seen = {}
def _best_game(i, j):
if j <= i:
return 0
elif (i, j) not in seen:
score_l = numbers[i] - _best_game(i+1, j)
score_r = numbers[j-1] - _best_game(i, j-1)
seen[(i, j)] = max(score_l, score_r)
return seen[(i, j)]
return _best_game(0, len(numbers))
This approach will be memory and time O(n^2).
Now bottom up.
def best_game(numbers):
# We start with scores for each 0 length game
# before, after, and between every pair of numbers.
# There are len(numbers)+1 of these, and all scores
# are 0.
scores = [0] * (len(numbers) + 1)
for i in range(len(numbers)):
# We will compute scores for all games of length i+1.
new_scores = []
for j in range(len(numbers) - i):
score_l = numbers[j] - scores[j+1]
score_r = numbers[j+i] - scores[j]
new_scores.append(max(score_l, score_r))
# And now we replace scores by new_scores.
scores = new_scores
return scores[0]
This is again O(n^2) time but only O(n) space. Because after I compute the games of length 1 I can throw away the games of length 0. Of length 2, I can throw away the games of length 1. And so on.
I am practising DP and I came across this question. http://www.spoj.com/problems/MPILOT/en/
Charlie acquired airline transport company and to stay in business he needs to lower the expenses by any means possible. There are N pilots working for his company (N is even) and N/2 plane crews needs to be made. A plane crew consists of two pilots - a captain and his assistant. A captain must be older than his assistant. Each pilot has a contract granting him two possible salaries - one as a captain and the other as an assistant. A captain's salary is larger than assistant's for the same pilot. However, it is possible that an assistant has larger salary than his captain. Write a program that will compute the minimal amount of money Charlie needs to give for the pilots' salaries if he decides to spend some time to make the optimal (i.e. the cheapest) arrangement of pilots in crews.
Input
The first line of input contains integer N, 2 ≤ N ≤ 10,000, N is even, the number of pilots working for the Charlie's company. The next N lines of input contain pilots' salaries. The lines are sorted by pilot's age, the salaries of the youngest pilot are given the first. Each of those N lines contains two integers separated by a space character, X i Y, 1 ≤ Y < X ≤ 100,000, a salary as a captain (X) and a salary as an assistant (Y).
Output
The first and only line of output should contain the minimal amount of money Charlie needs to give for the pilots' salaries.
After research, I found out this will be solved by DP but how exactly do I solve this? I have spent hours reading up on the links but I didn't get one which is easily understandable. Please help me.
There's actually a nice way to visualize it. Starting from the bottom left, the start of our ascending list, we can envision choosing a Y (assistant's salary) as a movement to the right and an X (captain's salary) as a movement up, with the condition that the southwest-northeast diagonal is not crossed (see Catalan Number in Wikipedia).
From this we can see that each node in the triangle has at most two predecessors, from the west or from the south, and so the bottom-up general case ought to be:
captain assistant
dp[i][j] = min(x[i+j-1] + dp[i-1][j], y[i+j-1] + dp[i][j-1])
Example:
x = [4,5,6,7]
y = [3,2,1,2]
[9+7]
[3+5] [min(8+1,5+6)]
[.] [3] [3+2]
I'll leave coding as an exercise.
As usual, we need to find a compact set of subproblems. Remembering the details of how the pilots are matched won't do -- we need something like the following characterization.
Consider a labeling that makes each pilot a captain or an assistant without regard to matching. There exists a valid matching if and only if the following two conditions hold:
There are N/2 captains and N/2 assistants.
For every age cutoff, there are at least as many assistants who are younger than the cutoff as there are captains who are younger than the cutoff.
The "only if" direction is easy: Condition 1 is obvious, and Condition 2 holds because the inequality holds for each 2-pilot crew, and we can sum the inequalities.
For the "if" direction, we actually have to construct crews. We proceed by induction. If there are no pilots, then the empty match is valid. Otherwise, since the youngest pilot is an assistant (by Condition 2) and there exists at least one captain (by Condition 1), there exists (by Sperner's lemma if you want to get fancy) a pair of pilots such that (a) no pilots are intermediate in age (b) the younger of the pair is an assistant (c) the older of the pair is a captain. Match the pair and remove them from the pool. Observe that both Conditions still hold, so match the rest by inductive hypothesis.
This observation leads to an O(N^2)-time dynamic program. We repeatedly read the salaries of the next oldest pilot and then compute, given that K pilots have been considered so far, for all C from 0 to [K/2], the minimum cost of paying K - C assistants and C captains among these pilots. At the end, return the cost of paying N/2 assistants and N/2 captains. Untested Python:
def cost(pilots):
cost = [0]
for i, (assistant_salary, captain_salary) in enumerate(pilots):
cost.append(float('inf')) # two-way sentinel
cost = [min(cost[c] + assistant_salary,
cost[c - 1] + captain_salary)
for c in range((i + 1) / 2 + 1)]
return cost[-1] # i.e., N/2
Let's formulate some conditions for a recursion: if we've reached the end, return the sum; if we have N/2 assistants, add a captain next; if we have the same number of assistants as captains left, add an assistant next (we can't have a captain younger than an assistant); otherwise, return the minimum cost of either adding a captain or an assistant.
JavaScript code:
var x = [4,5,6,7];
var y = [3,2,1,2];
var n = x.length;
function f(i,ys,s){
if (i == n){
return s;
}
if (ys == n / 2){
return f(i + 1,ys,s + x[i]);
} else if (i % 2 == 0 && ys == i / 2){
return f(i + 1,ys + 1,s + y[i]);
} else {
return Math.min(f(i + 1,ys + 1,s + y[i]),f(i + 1,ys,s + x[i]));
}
}
Output:
console.log(f(0,0,0)) // 16
For recursive approach in c++, you can follow this code for better understanding:
int min_salary(int a[],int n,int x,int c[])
{
if(n==0)
return 0;
if(x==0)
return(a[0]+min_salary(a+1,n-1,1,c+1));
if(x==n)
return(c[0]+min_salary(a+1,n-1,x-1,c+1));
else
return(min(a[0]+min_salary(a+1,n-1,x+1,c+1),c[0]+min_salary(a+1,n-1,x-1,c+1)));
}
I often teach large introductory programming classes (400 - 600 students) and when exam time comes around, we often have to split the class up into different rooms in order to make sure everyone has a seat for the exam.
To keep things logistically simple, I usually break the class apart by last name. For example, I might send students with last names A - H to one room, last name I - L to a second room, M - S to a third room, and T - Z to a fourth room.
The challenge in doing this is that the rooms often have wildly different capacities and it can be hard to find a way to segment the class in a way that causes everyone to fit. For example, suppose that the distribution of last names is (for simplicity) the following:
Last name starts with A: 25
Last name starts with B: 150
Last name starts with C: 200
Last name starts with D: 50
Suppose that I have rooms with capacities 350, 50, and 50. A greedy algorithm for finding a room assignment might be to sort the rooms into descending order of capacity, then try to fill in the rooms in that order. This, unfortunately, doesn't always work. For example, in this case, the right option is to put last name A in one room of size 50, last names B - C into the room of size 350, and last name D into another room of size 50. The greedy algorithm would put last names A and B into the 350-person room, then fail to find seats for everyone else.
It's easy to solve this problem by just trying all possible permutations of the room orderings and then running the greedy algorithm on each ordering. This will either find an assignment that works or report that none exists. However, I'm wondering if there is a more efficient way to do this, given that the number of rooms might be between 10 and 20 and checking all permutations might not be feasible.
To summarize, the formal problem statement is the following:
You are given a frequency histogram of the last names of the students in a class, along with a list of rooms and their capacities. Your goal is to divvy up the students by the first letter of their last name so that each room is assigned a contiguous block of letters and does not exceed its capacity.
Is there an efficient algorithm for this, or at least one that is efficient for reasonable room sizes?
EDIT: Many people have asked about the contiguous condition. The rules are
Each room should be assigned at most a block of contiguous letters, and
No letter should be assigned to two or more rooms.
For example, you could not put A - E, H - N, and P - Z into the same room. You could also not put A - C in one room and B - D in another.
Thanks!
It can be solved using some sort of DP solution on [m, 2^n] space, where m is number of letters (26 for english) and n is number of rooms. With m == 26 and n == 20 it will take about 100 MB of space and ~1 sec of time.
Below is solution I have just implemented in C# (it will successfully compile on C++ and Java too, just several minor changes will be needed):
int[] GetAssignments(int[] studentsPerLetter, int[] rooms)
{
int numberOfRooms = rooms.Length;
int numberOfLetters = studentsPerLetter.Length;
int roomSets = 1 << numberOfRooms; // 2 ^ (number of rooms)
int[,] map = new int[numberOfLetters + 1, roomSets];
for (int i = 0; i <= numberOfLetters; i++)
for (int j = 0; j < roomSets; j++)
map[i, j] = -2;
map[0, 0] = -1; // starting condition
for (int i = 0; i < numberOfLetters; i++)
for (int j = 0; j < roomSets; j++)
if (map[i, j] > -2)
{
for (int k = 0; k < numberOfRooms; k++)
if ((j & (1 << k)) == 0)
{
// this room is empty yet.
int roomCapacity = rooms[k];
int t = i;
for (; t < numberOfLetters && roomCapacity >= studentsPerLetter[t]; t++)
roomCapacity -= studentsPerLetter[t];
// marking next state as good, also specifying index of just occupied room
// - it will help to construct solution backwards.
map[t, j | (1 << k)] = k;
}
}
// Constructing solution.
int[] res = new int[numberOfLetters];
int lastIndex = numberOfLetters - 1;
for (int j = 0; j < roomSets; j++)
{
int roomMask = j;
while (map[lastIndex + 1, roomMask] > -1)
{
int lastRoom = map[lastIndex + 1, roomMask];
int roomCapacity = rooms[lastRoom];
for (; lastIndex >= 0 && roomCapacity >= studentsPerLetter[lastIndex]; lastIndex--)
{
res[lastIndex] = lastRoom;
roomCapacity -= studentsPerLetter[lastIndex];
}
roomMask ^= 1 << lastRoom; // Remove last room from set.
j = roomSets; // Over outer loop.
}
}
return lastIndex > -1 ? null : res;
}
Example from OP question:
int[] studentsPerLetter = { 25, 150, 200, 50 };
int[] rooms = { 350, 50, 50 };
int[] ans = GetAssignments(studentsPerLetter, rooms);
Answer will be:
2
0
0
1
Which indicates index of room for each of the student's last name letter. If assignment is not possible my solution will return null.
[Edit]
After thousands of auto generated tests my friend has found a bug in code which constructs solution backwards. It does not influence main algo, so fixing this bug will be an exercise to the reader.
The test case that reveals the bug is students = [13,75,21,49,3,12,27,7] and rooms = [6,82,89,6,56]. My solution return no answers, but actually there is an answer. Please note that first part of solution works properly, but answer construction part fails.
This problem is NP-Complete and thus there is no known polynomial time (aka efficient) solution for this (as long as people cannot prove P = NP). You can reduce an instance of knapsack or bin-packing problem to your problem to prove it is NP-complete.
To solve this you can use 0-1 knapsack problem. Here is how:
First pick the biggest classroom size and try to allocate as many group of students you can (using 0-1 knapsack), i.e equal to the size of the room. You are guaranteed not to split a group of student, as this is 0-1 knapsack. Once done, take the next biggest classroom and continue.
(You use any known heuristic to solve 0-1 knapsack problem.)
Here is the reduction --
You need to reduce a general instance of 0-1 knapsack to a specific instance of your problem.
So lets take a general instance of 0-1 knapsack. Lets take a sack whose weight is W and you have x_1, x_2, ... x_n groups and their corresponding weights are w_1, w_2, ... w_n.
Now the reduction --- this general instance is reduced to your problem as follows:
you have one classroom with seating capacity W. Each x_i (i \in (1,n)) is a group of students whose last alphabet begins with i and their number (aka size of group) is w_i.
Now you can prove if there is a solution of 0-1 knapsack problem, your problem has a solution...and the converse....also if there is no solution for 0-1 knapsack, then your problem have no solution, and vice versa.
Please remember the important thing of reduction -- general instance of a known NP-C problem to a specific instance of your problem.
Hope this helps :)
Here is an approach that should work reasonably well, given common assumptions about the distribution of last names by initial. Fill the rooms from smallest capacity to largest as compactly as possible within the constraints, with no backtracking.
It seems reasonable (to me at least) for the largest room to be listed last, as being for "everyone else" not already listed.
Is there any reason to make life so complicated? Why cann't you assign registration numbers to each student and then use the number to allocate them whatever the way you want :) You do not need to write a code, students are happy, everyone is happy.
I was just reading The Two Egg Problem:
The Two Egg Problem
You are given two eggs, and access to a 100-storey building. Both eggs are identical. The aim is to find out the highest floor from which an egg will not break when dropped out of a window from that floor. If an egg is dropped and does not break, it is undamaged and can be dropped again. However, once an egg is broken, that’s it for that egg.
If an egg breaks when dropped from floor n, then it would also have broken from any floor above that. If an egg survives a fall, then it will survive any fall shorter than that.
The question is: What strategy should you adopt to minimize the number egg drops it takes to find the solution?. (And what is the worst case for the number of drops it will take?)
I was following along until the "Look see I can do three" section. The author states that after the first egg breaks it degrades into the 2-egg problem and can be solved recursively.
That's great, but wouldn't we want to choose larger step-sizes when using 3 eggs instead of 2 (for the first egg)? From which floor do we throw the first egg?
With 1 egg, we have to start at floor 1.
With 2 eggs, we solve for n(n+1)/2=k and round up, where n is the starting floor, and k is the number of floors.
With 3... I'm having trouble coming up with a formula.
Thinking about this a bit more, with 2 eggs, the maximum number of drops is equal to the floor number that we drop our first egg from. For example, with 2 eggs and 100 floors, the solution is 14, which means we drop the first egg from floor 14, and if it breaks, we have to drop up to 13 more times, for floors 1-13.
With 3 eggs, the solution is 9 (as shown in the chart). But we wouldn't want to throw the first egg at floor 9, we can throw it higher, because we don't have to iterate by 1s in-between.
If we throw from floor 14 again, and it breaks, then we recurse. n(n+1)/2=k where k is now 13... but that gives us 4.815, if we ceil and that and add our previous drop we get 6, which is lower than the actual solution, so something here is wrong...
If we throw from floor 14 again, and it breaks, then we recurse. n(n+1)/2=k where k is now 13... but that gives us 4.815, if we ceil and that and add our previous drop we get 6, which is lower than the actual solution, so something here is wrong...
What if it doesn't break? Then you have a three-egg problem with 86 floors, which takes maybe one drop less to solve than the 100-floor problem.
Say you drop the first egg from the 50th floor. If it breaks, you have a two-egg problem with 49 floors, which takes up to 10 additional drops. So that would give you a worst-case of 11 drops (since if it doesn't break, the 50-floor three-egg problem takes at most 7 additional drops).
If you choose the 37th floor for the first drop, if it breaks, you have a 36-floor two-egg problem, needing up to 8 additional drops. If it doesn't break, you have a 63-floor three-egg problem left. You want to solve that problem with at most 8 drops, so if the next drop breaks the egg, the remaining two-egg problem should be solvable in at most 7 drops, thus the highest floor you can choose for the second drop is 37 + 28 + 1 = 66, since 28 floors is the highest you can solve with at most 7 drops and two eggs. If the egg doesn't break, you have a 34-floor three-egg problem with 7 drops left. The highest you can certainly solve with the remaining 6 drops if the egg breaks is 21 (6*7/2), so you can choose floor 66 + 21 + 1 = 88. If the egg doesn't break, you have 12 floors left with 6 drops, which is already doable with only two eggs.
Systematically, the highest number of floors you can certainly solve with d drops and e eggs is
/ 1, if d == 1
F(e,d) = | d, if e == 1
\ F(e-1,d-1) + 1 + F(e,d-1), if e > 1 and d > 1
If you have only one drop, you have no choice but to choose the lowest floor of which you do not yet know that the egg doesn't break. If that breaks it, and you tried a higher floor, you don't know the first floor to break the egg.
If you have only one egg, you have to check every floor in order until the egg breaks or you run out of drops.
Otherwise, if the first drop is from a floor higher than F(e-1,d-1) + 1, you possibly can't find the first breaking floor if the egg breaks. If the first drop is from a lower floor, you can't reach as high with d-1 drops if the egg doesn't break, so the first drop should be from floor F(e-1,d-1) + 1. If it breaks, you can solve with the remaining e-1 eggs and d-1 drops by assumption. If not, you can solve for the next F(e,d-1) floors with the remaining drops and eggs.
Conversely, to find how many drops you may need for f floors with e eggs, you have to find
D(e,f) = min { d | F(e,d) >= f }
You can find that by calculating the F(e,d) matrix, or you can use dynamic programming:
If you choose floor s for the first drop, if the egg breaks, you need up to D(e-1,s-1) drops to determine the floor. If the egg doesn't break, you need up to D(e,f-s) drops to determine the floor.
So the worst case for choosing floor s for the first drop is
WC(s,e,f) = 1 + max { D(e-1,s-1), D(e,f-s) }
and the best of the worst case is
D(e,f) = minimum { WC(s,e,f) | 1 <= s <= f }
(where of course D(e,0) = 0).
This problem can be solved with following 3 approaches (that I know) :
Dynamic Programming
Solution using Binary Search Tree
Solution by obtaining the direct mathematical formula for maximum number of floors that can be tested or covered with given number of eggs and given number of drops
Let me first define some symbols as follows:
e = number of eggs
f = number of floors in building
n = number of egg drops
Fmax(e, n) = maximum number of floors that can be tested with e eggs and n drops
The crux for dynamic programming approach lies in following recursive formula for Fmax:
Fmax(e, n) = 1 + Fmax(e-1, n-1) + fmax(e, n-1)
And the crux for obtaining the direct mathematical formula for Fmax lies in following recursive formula for Fmax:
Fmax(e, n) = { ∑Fmax(e-1,i) for i = 1 to n } - Fmax(e-1, n) + n
Alternative solution using Binary Search Tree (BST) is also possible for this problem. In order to facilitate our analysis, let us draw BST with slight modifications as follows:
1. If egg breaks then child node is drawn on left down side
2. If egg does not break then child node is drawn straight down side
If we draw BST with above kind of representation then width of the BST (i.e. number of vertical columns in BST) represents the number of eggs.
Any BST with f number of nodes, drawn with above kind of representation and subjected to the constraint width of BST <= e (number of eggs) is a solution but it may not be the optimal solution.
Hence obtaining the optimal solution is equivalent to obtaining the arrangement of nodes in BST with minimum height subjected to the constraint: width of BST <= e
For more details about all the above 3 approaches, check my blog at: 3 approaches for solving generalized egg drop problem
This is the same answer that I provided at Egg Drop Printing Solutions. I am providing it here for anyone who wants to see the whole decision tree and reasoning laid out.
# This uses dynamic programming to find the basic information.
def optimal_solution(floors, eggs):
# dp[drops][eggs] = max_floors
dp = []
# With no drops, we can do no floors
dp.append([0 for x in range(eggs+1)])
# With one drop and any eggs, we can do 1 floor
one_drop = [1 for _ in range(eggs+1)]
one_drop[0] = 0 # Except no eggs is still no floors
dp.append(one_drop)
# Keep adding drops until we have our answer
# Note, in python array element -1 is shorthand for the end of the array.
while dp[-1][eggs] < floors:
# 0 floors for 0 eggs. 1 more for one egg
next_drop = [0, dp[-1][1] + 1]
for i in range(2, eggs+1): # Python for 2..eggs
# The best we can do is drop at floor dp[-1][i-1].
# If the egg breaks, we can find the answer using that solution.
# If the egg holds, we can find another dp[-1][i] floors.
next_drop.append(dp[-1][i] + dp[-1][i-1])
dp.append(next_drop)
return dp
# This turns that optimal solution into a decision tree.
def dp_to_decision_tree(dp, floors, start_floor=None, eggs=None, drops=None):
# Figure out defaults if needed.
if start_floor is None:
start_floor = 0
if drops is None:
drops = len(dp) - 1
if eggs is None:
eggs = len(dp[0]) - 1
# Are we done?
if floors == start_floor:
return start_floor
elif dp[drops][eggs] < floors - start_floor:
return None
# Do we need all of our drops?
while floors - start_floor < dp[drops-1][eggs]:
drops -= 1
drop_at = start_floor + dp[drops-1][eggs-1]
if eggs == 1:
drop_at = start_floor + 1
if floors < drop_at:
drop_at = floors
return [
drop_at,
dp_to_decision_tree(dp, floors, drop_at, eggs, drops-1),
dp_to_decision_tree(dp, drop_at-1, start_floor, eggs-1, drops-1),
{'eggs': eggs, 'floor_range': (start_floor, floors)}
]
# This prints the decision tree in a human readable format.
def print_decision_tree(tree, indent="", label="start"):
if tree is None:
print(f"{indent}{label}: ?")
elif isinstance(tree, int):
print(f"{indent}{label}: {tree} found")
else:
print(f"{indent}{label}: {tree[0]} {tree[3]}")
print_decision_tree(tree[1], indent + " ", label="held")
print_decision_tree(tree[2], indent + " ", label="broke")
# And this calls the previous functions.
def print_optimal_decisions(floors, eggs):
print_decision_tree(
dp_to_decision_tree(
optimal_solution(floors, eggs), floors))
# And now we can try it.
print_optimal_decisions(36, 3)
You can use simple dynamic programming solution.
n - number of floors.
k - number of eggs.
D[n,k] = you answer (The number of minimum throws).
D[j,1] = n-1 for each 1 <= j <= n.
The main idea for calculate D[n,k] for k>1:
D[n,k] = maximum 1 <= j <= n-1 { maximum{ D[j,k-1]+1, D[n-j,k]+1 }.
The blog here explains the mathematical approach for 3 eggs with 1000 floors:
https://sankalpiitr.wordpress.com/2012/03/02/the-2-eggs-problem-extended-to-3-eggs/
According to this blog formula would be
P = N + ( N * ( N + 1 ) * ( N – 1 ) ) /6
where P is the number of floors and N is the minimum drops required.
So solving for P=100, we get N as 8.2
I came across this question
ADZEN is a very popular advertising firm in your city. In every road
you can see their advertising billboards. Recently they are facing a
serious challenge , MG Road the most used and beautiful road in your
city has been almost filled by the billboards and this is having a
negative effect on
the natural view.
On people's demand ADZEN has decided to remove some of the billboards
in such a way that there are no more than K billboards standing together
in any part of the road.
You may assume the MG Road to be a straight line with N billboards.Initially there is no gap between any two adjecent
billboards.
ADZEN's primary income comes from these billboards so the billboard removing process has to be done in such a way that the
billboards
remaining at end should give maximum possible profit among all possible final configurations.Total profit of a configuration is the
sum of the profit values of all billboards present in that
configuration.
Given N,K and the profit value of each of the N billboards, output the maximum profit that can be obtained from the remaining
billboards under the conditions given.
Input description
1st line contain two space seperated integers N and K. Then follow N lines describing the profit value of each billboard i.e ith
line contains the profit value of ith billboard.
Sample Input
6 2
1
2
3
1
6
10
Sample Output
21
Explanation
In given input there are 6 billboards and after the process no more than 2 should be together. So remove 1st and 4th
billboards giving a configuration _ 2 3 _ 6 10 having a profit of 21.
No other configuration has a profit more than 21.So the answer is 21.
Constraints
1 <= N <= 1,00,000(10^5)
1 <= K <= N
0 <= profit value of any billboard <= 2,000,000,000(2*10^9)
I think that we have to select minimum cost board in first k+1 boards and then repeat the same untill last,but this was not giving correct answer
for all cases.
i tried upto my knowledge,but unable to find solution.
if any one got idea please kindly share your thougths.
It's a typical DP problem. Lets say that P(n,k) is the maximum profit of having k billboards up to the position n on the road. Then you have following formula:
P(n,k) = max(P(n-1,k), P(n-1,k-1) + C(n))
P(i,0) = 0 for i = 0..n
Where c(n) is the profit from putting the nth billboard on the road. Using that formula to calculate P(n, k) bottom up you'll get the solution in O(nk) time.
I'll leave up to you to figure out why that formula holds.
edit
Dang, I misread the question.
It still is a DP problem, just the formula is different. Let's say that P(v,i) means the maximum profit at point v where last cluster of billboards has size i.
Then P(v,i) can be described using following formulas:
P(v,i) = P(v-1,i-1) + C(v) if i > 0
P(v,0) = max(P(v-1,i) for i = 0..min(k, v))
P(0,0) = 0
You need to find max(P(n,i) for i = 0..k)).
This problem is one of the challenges posted in www.interviewstreet.com ...
I'm happy to say I got this down recently, but not quite satisfied and wanted to see if there's a better method out there.
soulcheck's DP solution above is straightforward, but won't be able to solve this completely due to the fact that K can be as big as N, meaning the DP complexity will be O(NK) for both runtime and space.
Another solution is to do branch-and-bound, keeping track the best sum so far, and prune the recursion if at some level, that is, if currSumSoFar + SUM(a[currIndex..n)) <= bestSumSoFar ... then exit the function immediately, no point of processing further when the upper-bound won't beat best sum so far.
The branch-and-bound above got accepted by the tester for all but 2 test-cases.
Fortunately, I noticed that the 2 test-cases are using small K (in my case, K < 300), so the DP technique of O(NK) suffices.
soulcheck's (second) DP solution is correct in principle. There are two improvements you can make using these observations:
1) It is unnecessary to allocate the entire DP table. You only ever look at two rows at a time.
2) For each row (the v in P(v, i)), you are only interested in the i's which most increase the max value, which is one more than each i that held the max value in the previous row. Also, i = 1, otherwise you never consider blanks.
I coded it in c++ using DP in O(nlogk).
Idea is to maintain a multiset with next k values for a given position. This multiset will typically have k values in mid processing. Each time you move an element and push new one. Art is how to maintain this list to have the profit[i] + answer[i+2]. More details on set:
/*
* Observation 1: ith state depends on next k states i+2....i+2+k
* We maximize across this states added on them "accumulative" sum
*
* Let Say we have list of numbers of state i+1, that is list of {profit + state solution}, How to get states if ith solution
*
* Say we have following data k = 3
*
* Indices: 0 1 2 3 4
* Profits: 1 3 2 4 2
* Solution: ? ? 5 3 1
*
* Answer for [1] = max(3+3, 5+1, 9+0) = 9
*
* Indices: 0 1 2 3 4
* Profits: 1 3 2 4 2
* Solution: ? 9 5 3 1
*
* Let's find answer for [0], using set of [1].
*
* First, last entry should be removed. then we have (3+3, 5+1)
*
* Now we should add 1+5, but entries should be incremented with 1
* (1+5, 4+3, 6+1) -> then find max.
*
* Could we do it in other way but instead of processing list. Yes, we simply add 1 to all elements
*
* answer is same as: 1 + max(1-1+5, 3+3, 5+1)
*
*/
ll dp()
{
multiset<ll, greater<ll> > set;
mem[n-1] = profit[n-1];
ll sumSoFar = 0;
lpd(i, n-2, 0)
{
if(sz(set) == k)
set.erase(set.find(added[i+k]));
if(i+2 < n)
{
added[i] = mem[i+2] - sumSoFar;
set.insert(added[i]);
sumSoFar += profit[i];
}
if(n-i <= k)
mem[i] = profit[i] + mem[i+1];
else
mem[i] = max(mem[i+1], *set.begin()+sumSoFar);
}
return mem[0];
}
This looks like a linear programming problem. This problem would be linear, but for the requirement that no more than K adjacent billboards may remain.
See wikipedia for a general treatment: http://en.wikipedia.org/wiki/Linear_programming
Visit your university library to find a good textbook on the subject.
There are many, many libraries to assist with linear programming, so I suggest you do not attempt to code an algorithm from scratch. Here is a list relevant to Python: http://wiki.python.org/moin/NumericAndScientific/Libraries
Let P[i] (where i=1..n) be the maximum profit for billboards 1..i IF WE REMOVE billboard i. It is trivial to calculate the answer knowing all P[i]. The baseline algorithm for calculating P[i] is as follows:
for i=1,N
{
P[i]=-infinity;
for j = max(1,i-k-1)..i-1
{
P[i] = max( P[i], P[j] + C[j+1]+..+C[i-1] );
}
}
Now the idea that allows us to speed things up. Let's say we have two different valid configurations of billboards 1 through i only, let's call these configurations X1 and X2. If billboard i is removed in configuration X1 and profit(X1) >= profit(X2) then we should always prefer configuration X1 for billboards 1..i (by profit() I meant the profit from billboards 1..i only, regardless of configuration for i+1..n). This is as important as it is obvious.
We introduce a doubly-linked list of tuples {idx,d}: {{idx1,d1}, {idx2,d2}, ..., {idxN,dN}}.
p->idx is index of the last billboard removed. p->idx is increasing as we go through the list: p->idx < p->next->idx
p->d is the sum of elements (C[p->idx]+C[p->idx+1]+..+C[p->next->idx-1]) if p is not the last element in the list. Otherwise it is the sum of elements up to the current position minus one: (C[p->idx]+C[p->idx+1]+..+C[i-1]).
Here is the algorithm:
P[1] = 0;
list.AddToEnd( {idx=0, d=C[0]} );
// sum of elements starting from the index at top of the list
sum = C[0]; // C[list->begin()->idx]+C[list->begin()->idx+1]+...+C[i-1]
for i=2..N
{
if( i - list->begin()->idx > k + 1 ) // the head of the list is "too far"
{
sum = sum - list->begin()->d
list.RemoveNodeFromBeginning()
}
// At this point the list should containt at least the element
// added on the previous iteration. Calculating P[i].
P[i] = P[list.begin()->idx] + sum
// Updating list.end()->d and removing "unnecessary nodes"
// based on the criterion described above
list.end()->d = list.end()->d + C[i]
while(
(list is not empty) AND
(P[i] >= P[list.end()->idx] + list.end()->d - C[list.end()->idx]) )
{
if( list.size() > 1 )
{
list.end()->prev->d += list.end()->d
}
list.RemoveNodeFromEnd();
}
list.AddToEnd( {idx=i, d=C[i]} );
sum = sum + C[i]
}
//shivi..coding is adictive!!
#include<stdio.h>
long long int arr[100001];
long long int sum[100001];
long long int including[100001],excluding[100001];
long long int maxim(long long int a,long long int b)
{if(a>b) return a;return b;}
int main()
{
int N,K;
scanf("%d%d",&N,&K);
for(int i=0;i<N;++i)scanf("%lld",&arr[i]);
sum[0]=arr[0];
including[0]=sum[0];
excluding[0]=sum[0];
for(int i=1;i<K;++i)
{
sum[i]+=sum[i-1]+arr[i];
including[i]=sum[i];
excluding[i]=sum[i];
}
long long int maxi=0,temp=0;
for(int i=K;i<N;++i)
{
sum[i]+=sum[i-1]+arr[i];
for(int j=1;j<=K;++j)
{
temp=sum[i]-sum[i-j];
if(i-j-1>=0)
temp+=including[i-j-1];
if(temp>maxi)maxi=temp;
}
including[i]=maxi;
excluding[i]=including[i-1];
}
printf("%lld",maxim(including[N-1],excluding[N-1]));
}
//here is the code...passing all but 1 test case :) comment improvements...simple DP