How do I solve this dynamic programming? - algorithm

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)));
}

Related

Understanding Egg Drop Algorithm for more than two eggs.

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]
}

How to improve this Dynamic programming solution(Optimisation in algorithm)

The Problem statement:
Assurance Company of Moving (ACM) is a company of moving things for people. Recently, some schools want to move their computers to another place. So they ask ACM to help them. One school reserves K trucks for moving, and it has N computers to move. In order not to waste the trucks, the school ask ACM to use all the trucks. That is to say, there must be some computers in each truck, and there are no empty trucks. ACM wants to know how many partition shemes exists with moving N computers by K trucks, the ACM ask you to compute the number of different shemes with given N and K. You needn't care with the order. For example N=7,K=3, the the following 3 partition instances are regarded as the same one and should be counted as one sheme: "1 1 5","1 5 1","5 1 1". Each truck can carry almost unlimited computers!!
Save Time :
You have to count how many sequences a[1..k] exist such that :
1) a[i] + a[2] + .... + a[k] = N such that permutations dont matter
My O(N*K^2) solution (Cannot figure out how to improve on it)
#include<assert.h>
#include<stdio.h>
#include<algorithm>
using namespace std;
int DP[5001][5001];
void ini()
{
int i,j,k;
DP[0][0]=1;
for(k=1;k<=500;k++)
for(j=1;j<=500;j++)
for(i=1;i<=500;i++)
{
DP[i][j]+=j>=k?DP[i-1][j-k]:0;
DP[i][j]%=1988;
}
return ;
}
int main()
{
ini();
int N,K,i,j;
while(1)
{
scanf("%d%d",&N,&K);
if(N==0 && K==0)
return 0;
int i;
if(DP[K][N]==0)
{assert(0);}
printf("%d\n",DP[K][N]);
}
return 0;
}
Explanation of my solution DP[i][j] represents the number of ways I can have total j computers using i Trucks only.
The k represents the number of computers with which I am dealing with that means I am just avoiding permutations!
How can I improve it to O(N*K)?
Problem constraints
N (1<=N<=5000) and K(1<=K<=N)
Problem Link: Problem Spoj
Just say that you have K gift boxes and N chocolates.
I will start with a recursive and real easy to convert it to iterative solution.
The key to avoid repetitions is distributing chocolates in a ascending order (descending also works). So you 7 chocolates and I put 2 chocolate in the first box, I will put at least 2 in the second box. WHY? this helps in avoiding repetitions.
now onwards TCL = totalChocholatesLeft & TBL = totalBinsLeft
So S(TCL,TBL) = S(TCL-TBL,TBL) + S(TCL,TBL-1);
you have to call the above expression starting with S(n-k), k)
Why? because all boxes need at least one item so first put `1` each box.
Now you are left with only `n-k` chocolates.
That's all! that's the DP recursion.
How does it work?
So in order to remove repetitions we are maintaning the ascending order.
What is the easiest way to maintain the ascending order ?
If you put 1 chocolate in the ith box, put 1 in all boxes in front of it i+1, i++2 .....k.
So after keeping chocolate in a gift box, you have two choices :
Either you want to continue with current box :
S(TCL-TBL,TBL) covers this
or to move the next box just never consider this box again
S(TCL,TBL-1) covers this.
Equivalent DP would make have TC : O(NK)
This problem is equivalent to placing n-k identical balls (after already placing one ball in each cell to make sure it's not empty) in k identical cells.
This can be solved using the recurrence formula:
D(n,0) = 0 n > 0
D(n,k) = 0 n < 0
D(n,1) = 1 n >= 0
D(n,k) = D(n,k-1) + D(n-k,k)
Explanation:
Stop clauses:
D(n,0) - no way to put n>0 balls in 0 cells
D(n<0,k) - no way to put negative number of balls in k cells
D(n,1) - one way to put n balls in 1 cell: all in this cell
Recurrence:
We have two choices.
We either have one (or more) empty cell, so we recurse with the same problem, and one less cell: D(n,k-1)
Otherwise, we have no empty cells, so we put one ball in each cell, recurse with the same number of cells and k less balls, D(n-k,k)
The two possibilities are of disjoint sets, so the union of both sets is the summation of the two sizes, thus D(n,k) = D(n,k-1) + D(n-k,k)
The above recursive formula is easy to compute in O(1) (assuming O(1) arithmetics), if the "lower" problems are known, and the DP solution needs to fill a table of size (n+1)*(k+1), so this solution is O(nk)

Finding an arrangement that yield minimum salary

Once again I am stuck with a sopj problem pilots
The problem statement is..
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.
sample
input
4
5000 3000
6000 2000
8000 1000
9000 6000
output
19000
input
6
10000 7000
9000 3000
6000 4000
5000 1000
9000 3000
8000 6000
output
32000
Now i am using a greedy approch as it is clear that first pilot should be assistant and after that i check if it will be possible to save money by making the pilot captain if yes then i will make him a captain else an assistant.
It is perfectly working for most of the cases but I get a wrong awnser.
my code is..
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define FOR(i,n) for(int i=0;i<n;i++)
int main()
{
int n;
//freopen("input.txt","r",stdin);
cin>>n;
vector<pair<pii,int> > data;
FOR(i,n)
{
int csal,asal;
cin>>csal>>asal;
int diff=csal-asal;
data.pb(mp(mp(csal,asal),diff));
}
int ccount=0,acount=0,sal=0;
FOR(i,n)
{
if(acount<n/2)
{
int flag=1;
for(int j=i+1;j<=i+(acount-ccount);j++)
{
if(data[i].se<=data[j].se)
{
//cout<<j<<" ";
flag=0;
break;
}
}
if(flag)
{
sal+=data[i].fi.se;
acount++;
}
else
{
sal+=data[i].fi.fi;
ccount++;
}
}
else
{
sal+=data[i].fi.fi;
ccount++;
}
//cout<<sal<<" "<<i<<"\n";
}
cout<<sal<<"\n";
return 0;
}
please help me to solve this problem..
Here is a solution with O(N log N) running time. It uses greedy algorithm, but it is different from yours.
1)Let's assume that delta[i] = X[i] - Y[i].
2)Now let's process pilots sorted by delta[i] in descending order. We will assume that all pilots are initially given captain positions.
3)Each pilot should be reassigned to assistant position if it is possible.
That's it. I claim that this algorithm always gives a correct result.
Proof:
1)When a pilot cannot be given an assistant job?
i)When there are no "free" captains to the right(in terms of age) of him(that is, if the number of captains to the right of him is greater than or equal to the number of assistants). Why could this happen? Only if there are "too many" assistants to the right of him. Let's assume that we decided to fix it. It would require changing one of the assistants to the captain. But if a pilot is an assistant, he was processed before the current pilot. It means that his delta is greater(recall that we iterate over them in sorted order). One more observation: one assistant can block only one captain(because the crew includes only one captain). These two observation show that changing something when it is not possible to make current pilot an assistant would only make the answer worse.
ii)When he is taken as a captain by an assistant to the left(in terms of age) from him. To fix it, it would be necessary to make that guy a captain again, like in i). It would make the answer only worse(the proof is the same as in the situation above).
2)Using mathematical induction and two observations above, it can be proven rigorously that this algorithm is correct.
3)This algorithm will always produce exactly N / 2 assistants because it makes a pilot an assistant whenever possible and there are exactly N / 2 possibilities to do it.
The only question remaining is: how to check if it is possible to make the current pilot an assistant?
Here is a fast way: let's assign each pilot 1 if he is a captain and -1 otherwise. Let's take a look at this array of -1 and 1 in order of pilots' ages. I claim that if there is a suffix with negative sum, the configuration is invalid. Otherwise, it is valid(this statement is not that hard to prove, but I will not post the proof here or there will too much proofs in my answer). So we need to maintain two operations: change -1 to 1(and vice versa) and tell if there is a suffix with negative sum. It is a standard problem and can be solved with segment tree(you can store total sum and minimum suffix sum in each node, update is easy because the value of only one element is changed at a time(that is, a value in a leaf)).
The complexity is O(N log N) because we need to sort the array and during the iteration at most 3 queries to a segment tree are done per one step(and each query takes O(log N) time).

How to efficiently detect a tie early in m,n,k-game (generalized tic-tac-toe)?

I'm implementing an m,n,k-game, a generalized version of tic-tac-toe, where m is the number of rows, n is the number of columns and k is the number of pieces that a player needs to put in a row to win. I have implemented a check for a win, but I haven't figured out a satisfactory way to check before the board is full of pieces, if no player can win the game. In other words, there might be empty slots on the board, but they cannot be filled in such a way that one player would win.
My question is, how to check this efficiently? The following algorithm is the best that I can think of. It checks for two conditions:
A. Go over all board positions in all 4 directions (top to bottom, right to left, and both diagonal directions). If say k = 5, and 4 (= k-1) consecutive empty slots are found, stop checking and report "no tie". This doesn't take into account for example the following situation:
OX----XO (Example 1)
where a) there are 4 empty consecutive slots (-) somewhere between two X's, b) next it is O's turn, c) there are less than four other empty positions on the board and no player can win by putting pieces to those, and d) it is not possible to win in any other direction than horizontally in the shown slots either. Now we know that it is a tie because O will eventually block the last winning possibility, but erroneously it is not reported yet because there are four consecutive empty slots. That would be ok (but not great). Checking this condition gives a good speed-up at the beginning when the checking algorithm usually finds such a case early, but it gets slower as more pieces are put on the board.
B. If this k-1-consecutive-empty-slots-condition isn't met, the algorithm would check the slots again consecutively in all 4 directions. Suppose we are currently checking from left to right. If at some point an X is encountered and it was preceded by an O or - (empty slot) or a board border, then start counting the number of consecutive X's and empty slots, counting in this first encountered X. If one can count to 5, then one knows it is possible for X to win, and "no tie" is reported. If an O preceded by an X is encountered before 5 consecutive X's, then X cannot win in those 5 slots from left to right starting from where we started counting. For example:
X-XXO (Example 2)
12345
Here we started checking at position 1, counted to 4, and encountered an O. In this case, one would continue from the encountered O in the same way, trying to find 5 consecutive O's or empty slots this time. In another case when counting X's or empty slots, an O preceded by one or more empty slots is encountered, before counting to 5. For example:
X-X-O (Example 3)
12345
In this case we would again continue from the O at position 5, but add to the new counter (of consecutive O's or empty slots) the number of consecutive empty slots that preceded O, here 1, so that we wouldn't miss for example this possible winning position:
X-X-O---X (Example 4)
In this way, in the worst case, one would have to go through all positions 4 times (4 directions, and of course diagonals whose length is less than k can be skipped), giving running time O(mn).
The best way I could think of was doing these two described checks, A and B, in one pass. If the checking algorithm gets through all positions in all directions without reporting "no tie", it reports a tie.
Knowing that you can check a win just by checking in the vicinity of the last piece that was added with running time O(k), I was wondering if there were quicker ways to do an early check for a tie. Doesn't have to be asymptotically quicker. I'm currently keeping the pieces in a two-dimensional array. Is there maybe a data structure that would allow an efficient check? One approach: what is the highest threshold of moves that one can wait the players to make before running any checks for a tie at all?
There are many related questions at Stack Overflow, for example this, but all discussions I could find either only pointed out the obvious tie condition, where the number of moves made is equal to the size of the board (or they checked if the board is full), or handled only the special case where the board is square: m = n. For example this answer claims to do the check for a tie in constant time, but only works when m = n = k. I'm interested in reporting the tie as early as possible and for general m,n and k. Also if the algorithm works for more than two players, that would be neat.
I would reduce the problem of determining a tie to the easier sub-problem:
Can player X still win?
If the answer is 'no' for all players, it is a tie.
To find out whether Player X can win:
fill all blank spaces with virtual 'X'-pieces
are there k 'X'-pieces in a row anywhere?
if there are not --> Player X cannot win. return false.
if there are, find the row of k stones with the least amount of virtual pieces. Count the number of virtual pieces in it.
count the number of moves player X has left, alternating with all other players, until the board is completely full.
if the number of moves is less than the amount of virtual pieces required to win, player X cannot win. return false.
otherwise, player X can still win. return true.
(This algorithm will report a possible win for player X even in cases where the only winning moves for X would have another player win first, but that is ok, since that would not be a tie either)
If, as you said, you can check a win just by checking in the vicinity of the last piece that was added with running time O(k), then I think you can run the above algorithm in O(k * Number_of_empty_spots): Add all virtual X-Piece, note any winning combinations in the vicinity of the added pieces.
The number of empty slots can be large, but as long as there is at least one empty row of size k and player X has still k moves left until the board is filled, you can be sure that player X can still win, so you do not need to run the full check.
This should work with any number of players.
Actually the constant time solution you referenced only works when k = m = n as well. If k is smaller then I don't see any way to adapt the solution to get constant time, basically because there are multiple locations on each row/column/diagonal where a winning consecutive k 0's or 1's may occur.
However, maintaining auxiliary information for each row/column/diagonal can give a speed up. For each row/column/diagonal, you can store the start and end locations for consecutive occurrences of 1's and blanks as possible winning positions for player 1, and similarly store start and end locations of consecutive occurrences of 0's and blanks as possible winning positions for player 0. Note that for a given row/column/diagonal, intervals for player 0 and 1 may overlap if they contain blanks. For each row/column/diagonal, store the intervals for player 1 in sorted order in a self-balancing binary tree (Note you can do this because the intervals are disjoint). Similarly store the intervals for player 0 sorted in a tree. When a player makes a move, find the row/column/diagonals that contain the move location and update the intervals containing the move in the appropriate row column and diagonal trees for the player that did not make the move. For the player that did not make a move, this will split an interval (if it exists) into smaller intervals that you can replace the old interval with and then rebalance the tree. If an interval ever gets to length less than k you can delete it. If a tree ever becomes empty then it is impossible for that player to win in that row/column/diagonal. You can maintain a counter of how many rows/columns/diagonals are impossible to win for each player, and if the counter ever reaches the total number of rows/columns/diagonals for both players then you know you have a tie. The total running time for this is O(log(n/k) + log(m/k)) to check for a tie per move, with O(mn/k) extra space.
You can similarly maintain trees that store consecutive intervals of 1's (without spaces) and update the trees in O(log n + log m) time when a move is made, basically searching for the positions before and after the move in your tree and updating the interval(s) found and merging two intervals if two intervals (before and after) are found. Then you report a win if an interval is ever created/updated and obtains length greater than or equal to k. Similarly for player 0. Total time to check for a win is O(log n + log m) which may be better than O(k) depending on how large k is. Extra space is O(mn).
Let's look at one row (or column or diagonal, it doesn't matter) and count the number of winning lines of length k ("k-line") it's possible to make, at each place in the row, for player X. This solution will keep track of that number over the course of the game, checking fulfillment of the winning condition on each move as well as detecting a tie.
1 2 3... k k k k... 3 2 1
There is one k-line including an X in the leftmost slot, two with the second slot from the left, and so on. If an opposing player, O or otherwise, plays in this row, we can reduce the k-line possibility counts for player X in O(k) time at the time of the move. (The logic for this step should be straightforward after doing an example, needing no other data structure, but any method involving checking each of the k rows of k from will do. Going left to right, only k operations on the counts is needed.) An enemy piece should set the possibility count to -1.
Then, a detectably tied game is one where no cell has a non-zero k-line possibility count for any player. It's easy to check this by keeping track of the index of the first non-zero cell. Maintaining the structure amounts to O(k*players) work on each move. The number of empty slots is less than those filled, for positions that might be tied, so the other answers are good for checking a position in isolation. However, at least for reasonably small numbers of players, this problem is intimately linked with checking the winning condition in the first place, which at minimum you must do, O(k), on every move. Depending on your game engine there may be a better structure that is rich enough to find good moves as well as detect ties. But the possibility counting structure has the nice property that you can check for a win whilst updating it.
If space isn't an issue, I had this idea:
For each player maintain a structure sized (2mn + (1 - k)(m + n) + 2(m - k + 1)(n - k + 1) + 2(sum 1 to (m - k))) where each value represents if one of another player's moves are in one distinct k-sized interval. For example for a 8-8-4 game, one element in the structure could represent row 1, cell 0 to 3; another row 1, cell 1 to 4; etc.
In addition, one variable per player will represent how many elements in their structure are still unset. Only one move is required to set an element, showing that that k-interval can no longer be used to win.
An update of between O(k) and O(4k) time per player seems needed per move. A tie is detected when the number of players exceeds the number of different elements unset.
Using bitsets, the number of bytes needed for each player's structure would be the structure size divided by 8. Notice that when k=m=n, the structure size is 4*k and update time O(4). Less than half a megabyte per player would be needed for a 1000,1000,5 game.
Below is a JavaScript example.
var m = 1000, n = 1000, k = 5, numberOfPlayers = 2
, numberOfHorizontalKIs = m * Math.max(n - k + 1,0)
, numberOfverticalKIs = n * Math.max(m - k + 1,0)
, horizontalVerticalKIArraySize = Math.ceil((numberOfHorizontalKIs + numberOfverticalKIs)/31)
, horizontalAndVerticalKIs = Array(horizontalVerticalKIArraySize)
, numberOfUnsetKIs = horizontalAndVerticalKIs
, upToM = Math.max(0,m - k) // southwest diagonals up to position m
, upToMSum = upToM * (upToM + 1) / 2
, numberOfSouthwestKIs = 2 * upToMSum //sum is multiplied by 2 to account for bottom-right-corner diagonals
+ Math.max(0,n - m + 1) * (m - k + 1)
, diagonalKIArraySize = Math.ceil(2 * numberOfSouthwestKIs/31)
, diagonalKIs = Array(diagonalKIArraySize)
, numberOfUnsetKIs = 2 * numberOfSouthwestKIs + numberOfHorizontalKIs + numberOfverticalKIs
function checkTie(move){
var row = move[0], column = move[1]
//horizontal and vertical
for (var rotate=0; rotate<2; rotate++){
var offset = Math.max(k - n + column, 0)
column -= offset
var index = rotate * numberOfHorizontalKIs + (n - k + 1) * row + column
, count = 0
while (column >= 0 && count < k - offset){
var KIArrayIndex = Math.floor(index / 31)
, bitToSet = 1 << index % 31
if (!(horizontalAndVerticalKIs[KIArrayIndex] & bitToSet)){
horizontalAndVerticalKIs[KIArrayIndex] |= bitToSet
numberOfUnsetKIs--
}
index--
column--
count++
}
//rotate board to log vertical KIs
var mTmp = m
m = n
n = mTmp
row = move[1]
column = move[0]
count = 0
}
//rotate board back
mTmp = m
m = n
n = mTmp
// diagonals
for (var rotate=0; rotate<2; rotate++){
var diagonalTopColumn = column + row
if (diagonalTopColumn < k - 1 || diagonalTopColumn >= n + m - k){
continue
} else {
var offset = Math.max(k - m + row, 0)
row -= offset
column += offset
var dBeforeM = Math.min (diagonalTopColumn - k + 1,m - k)
, dAfterM = n + m - k - diagonalTopColumn
, index = dBeforeM * (dBeforeM + 1) / 2
+ (m - k + 1) * Math.max (Math.min(diagonalTopColumn,n) - m + 1,0)
+ (diagonalTopColumn < n ? 0 : upToMSum - dAfterM * (dAfterM + 1) / 2)
+ (diagonalTopColumn < n ? row : n - 1 - column)
+ rotate * numberOfSouthwestKIs
, count = 0
while (row >= 0 && column < n && count < k - offset){
var KIArrayIndex = Math.floor(index / 31)
, bitToSet = 1 << index % 31
if (!(diagonalKIs[KIArrayIndex] & bitToSet)){
diagonalKIs[KIArrayIndex] |= bitToSet
numberOfUnsetKIs--
}
index--
row--
column++
count++
}
}
//mirror board
column = n - 1 - column
}
if (numberOfUnsetKIs < 1){
return "This player cannot win."
} else {
return "No tie."
}
}

A Dynamic programming problem

Can anyone help me find an optimal Dynamic programming algorithm for this problem
On the way to dinner, the CCC competitors are lining up for their delicious curly fries. The N (1 ≤ N ≤ 100) competitors have lined up single-file to enter the cafeteria.
Doctor V, who runs the CCC, realized at the last minute that programmers simply hate standing in line next to programmers who use a different language. Thankfully, only two languages are allowed at the CCC: Gnold and Helpfile. Furthermore, the competitors have decided that they will only enter the cafeteria if they are in a group of at least K (1 ≤ K ≤ 6) competitors.
Doctor V decided to iterate the following scheme:
* He will find a group of K or more competitors who use the same language standing next to each other in line and send them to dinner.
* The remaining competitors will close the gap, potentially putting similar-language competitors together.
So Doctor V recorded the sequence of competitors for you. Can all the competitors dine? If so, what is the minimum number of groups of competitors to be sent to dinner?
Input
The first line contains two integers N and K.
The second line contains N characters that are the sequence of competitors in line (H represents Helpfile, G represents Gnold)
Output
Output, on one line, the single number that is the minimum number of groups that are formed for dinner. If not all programmers can dine, output -1.
I'd prefer not to solve an SPOJ problem in a practical manner for you, so take the following as an existence proof of a poly-time DP.
For K fixed, the set of strings that can dine is context-free. I'm going to use g and h instead of G and H. For example, for K = 3, one grammar looks like
S -> ε | g S g S g S G | h S h S h S H
G -> ε | g S G
H -> ε | h S H
The idea is that either there are no diners, or the first diner dines with at least K - 1 others, between any two of which (and the last and the end) there is a string that can dine.
Now use the weighted variant of CYK to find the minimum-weight parse, where nonempty S productions have weight 1, and all others have weight 0. For K fixed, the running time of CYK is O(N3).
The subproblem is the minimal groups needed to get everyone to dine for a given state of the line. There are a whole lot of possible line states but only a few that will actually be seen, so your memoization should probably be done with a hash map.
Here's some pseudocode.
int dine(string line){
if(hashmap.contains(line)){
return hashmap.get(line);
}
if(line.length == 0){
return 0;
}
best = N+1;
for(i=0;i<line.length;i=j){
type = string[i];
j = i+1;
while(type == string[j]){
j++;
}
if(j-i >= K){
result = dine(string.substring(0,i-1) + string.substring(j,string.length));
if(result > 0 && result < best){
best = result;
}
}
}
if(best == N+1){
hashmap.insert(line, -1);
return -1;
}
else {
hashmap.insert(line, best+1);
return best+1;
}
}
If you already found answer for this line, return that answer. If theres no people in the line, you are already done and you don't need to form any more groups.
Assume you can't form any groups. Then try to prove this wrong by trying out all the contiguous groups of like-minded programmers in the line. If the group is big enough to get picked, see how many more moves it would take to get everyone in the resturaunt after removing this group. Keep track of the least moves needed.
If you couldn't find a way to remove all the groups, return -1. Otherwise, return the least moves needed after removing a group plus one to account for the move you made on this step.
How about divide and conquer? Take a (removable) group somewhere near the middle, and the two groups either side of it, say ...HHHGGGGGHHHHH.... - now there's two possibilities. Either those 2 sets of H's dine in the same group, or they don't. If they dine in the same group, then those G's between them must be removed as a group of precisely those G's (so you may as well try that as your first move). If they don't, then you can solve for the left and right sublists separately. Either case, you've got a shorter list to recurse on.

Resources