Finding the shortest path in a tree - algorithm

I'm having troubles to find a solution to the following question.
Suppose a company needs to have a machine over the next five year period. Each new machine costs $100,000. The annual cost of operating a machine during its ith year of operation is given as follows: C1 = $6000, C2 = $8000 and C3 = $12,000. A machine may be kept up to three years before being traded in. This means that a machine can be either kept or traded with in the first two years and has to be traded when its age is three. The trade in value after i years is t1= $80,000, t2 = $60,000 and t3 = $50,000. How can the company minimize costs over the five year period (year 0 to year 5) if the company is going to buy a new machine in the year 0?
Devise an optimal solution based on dynamic programming.
This problem can be represent using a tree. Here's the diagram.
Now I think that finding the shortest path in the above tree will give me the optimal solution. But I have no idea how to do that. Here are my questions,
Is there a classic problem regarding this question? (Like Travelling salesman problem or Change-making problem)
If yes, then What is it? What are the methods to solve it?
If not, then how to solve this problem.
Any other suggestions are also welcome.
Guys, I want some guidance and help for this question. (Do NOT think this as a request to get my homework done from you.) I have found a full Java implementation for this question here. But it does not use dynamic programming to solve the problem. Thank you in advance.

The company wants to minimize the cost over 5 year period. In the year 0 they are going to buy a machine & each year they have to decide whether the machine is being kept or traded. In order to arrive at an optimal solution, we have to make a set of choices at each end of the year. As we make each choice, sub problems of the same from often arise.
Thus we arrives at a position where given sub problem may arise from more than one partial set of choices. When devising an optimal solution based on dynamic programming, we can solve the problem by combining the solutions to sub problems.
Let the stages correspond to each year. The state is the age of the machine for that year. The decisions are whether to keep the machine or trade it in for a new one. Let Ft(x) be the minimum cost incurred from time t to time 5, given the machine is x years old in time t.
Base cases:
Since we have to trade machine in the end of 5 year F5(x)=-S[x]
In the year 0 we buy a new machine F0(1)=N+M[1]+F1(0)
In the range of 5 years and at most 3 years of age : 0
Keep existing machine at most 3 years : Ft(3)=N+M[0]+Ft+1(0) ; t≠0,1,2
def Fxy(self,time,age):
if self.Matrix[time][age]==None: <- Overlaping subproblems avoided
if(time>5 or age>2):
return 0
if time==5:
self.Matrix[time][age]=T=self.S[age]
self.Flag[time][age]='TRADE'
elif time==0:
self.Matrix[time][age]=K=self.N+self.M[0]+self.Fxy(time+1,time)
self.Flag[time][age]='KEEP'
elif time==3 and age==2:
self.Matrix[time][age]=T=self.S[age]+self.N+self.M[0]+self.Fxy(time+1,0)
self.Flag[time][age]='TRADE'
else:
T=self.S[age]+self.N+self.M[0]+self.Fxy(time+1,0)
if age+1<len(self.Matrix[0]):
K=self.M[age+1]+self.Fxy(time+1,age+1)
else:
K=self.M[age+1]
self.Matrix[time][age]=min(T,K)
if(self.Matrix[time][age]==T and self.Matrix[time][age]==K):
self.Flag[time][age]='TRADE OR KEEP'
elif(self.Matrix[time][age]==T):
self.Flag[time][age]='TRADE'
else:
self.Flag[time][age]='KEEP'
return self.Matrix[time][age]
else:
return self.Matrix[time][age]
Optimal solutions can be achieved via drawing a decisions tree contains all possible paths & takes the minimum cost paths. We use a recursive algorithm where it traverses each tree level & make the path where current decision point occurs.
Ex: When it traverses F1(0), it has ‘TRADE OR KEEP’ decision binds with it. Then we can traverse two possible paths. When it traverses F2(1), since it has ‘KEEP’ decision then recursively we traverse F3(2), the right child. When ‘TRADE’ met, the left child continuously until it reaches the leaves.
def recursePath(self,x,y):
if(x==5):
self.dic[x].append(self.Flag[x][y])
return self.Flag[x][y]
else:
if(self.Flag[x][y]=='TRADE OR KEEP'):
self.recursePath(x+1,y)
self.recursePath(x+1,y+1)
if(self.Flag[x][y]=='KEEP'):
self.recursePath(x+1,y+1)
if(self.Flag[x][y]=='TRADE'):
self.recursePath(x+1,y)
self.dic[x].append(self.Flag[x][y])
return self.Flag[x][y]

If you want something like Dijkstra, why don't you just do Dijkstra? You'd need to change a few things in your graph interpretation but it seems very much doable:
Dijkstra will settle nodes according to a minimum cost criterium. Set that criterium to be "money lost by company". You will also settle nodes in Dijkstra and you need to determine what exactly will be a node. Consider a node to be a time and a property state e.g. year 4 with a working machine of x years old. In year 0, the money lost will be 0 and you will have no machine. You then add all possible edges/choices/state transitions, here being 'buy a machine'. You end up with a new node on the Dijkstra PQ [year 1, working machine of age 1] with a certain cost.
From thereon, you can always sell the machine (yielding a [year 1, no machine] node), buy a new machine [year 1, new machine]) or continue with the same ([year 2, machine of age 2]). You just continue to develop that shortest path tree untill you have everything you want for year 5 (or more).
You then have a set of nodes [year i, machine of age j]. To find the optimum for your company at year i, just look among all possibilities for it (I think it will always be [year i, no machine]) to get your answer.
As Dijkstra is an all-pairs shortest path algorithm, it gives you all best paths to all years
edit: some pseudo code for java
first you should create a node object/class to hold your node information.
Node{
int cost;
int year;
int ageOfMachine;
}
Then you could just add nodes and settle them. Make sure your PQ is sorting the nodes based on the cost field. Starting at the root:
PQ<Node> PQ=new PriorityQueue<Node>();
Node root= new Root(0,0,-1);//0 cost, year 0 and no machine)
PQ.offer(root);
int [] best= new int[years+1];
//set best[0..years] equal to a very large negative number
while(!PQ.isEmpty()){
Node n=PQ.poll();
int y=n.year;
int a=n.ageOfMachine;
int c=n.cost;
if(already have a cost for year y and machine of age a)continue;
else{
add [year y, age a, cost c] to list of settled nodes;
//examine all possible further actions
//add nodes for keeping a machine and trading a machine
PQ.offer(new Node(cost+profit selling current-cost of new machine,year+1,1));
PQ.offer(new Node(cost,year+1,age+1);//only if your machine can last an extra year
//check to see if you've found the best way of business in year i
if(cost+profit selling current>best[i])best[i]=cost+profit selling current;
}
}
Something along those lines will give you the best practice to reach year i with cost best[i]

I think I have found a simpler dynamic program solution.
Suggest Cost(n) is the whole cost when sell at year n . And the cost of keeping a machine for 1, 2 ,3 year is cost1,cost2,cost3 ( which is 26000, 54000, 76000 in this problem ) .
Then we can divide the problem to sub-problems like this:
**Cost(n)= MIN( Cost(n-1)+cost1, Cost(n-2)+cost2, Cost(n-3)+cost3 );**
So we can calculate it in 'bottom-up way', which is just O(n).
I have implemented and tested it using C :
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct costAndSell_S{
int lastSellYear;
int cost;
};
int operatCost[3]={6000,8000,12000};
int sellGain[3]={80000,60000,50000};
int newMachinValue=100000;
int sellCost[3];
struct costAndSell_S costAndSell[20];
void initSellCost(){
memset( costAndSell, 0, sizeof(costAndSell));
sellCost[0]=operatCost[0]+newMachinValue-sellGain[0];
sellCost[1]=operatCost[0]+operatCost[1]+newMachinValue-sellGain[1];
sellCost[2]=operatCost[0]+operatCost[1]+operatCost[2]+newMachinValue-sellGain[2];
costAndSell[0].cost=100000;
return;
}
int sellAt( int year ){
if ( year<0){
return(costAndSell[0].cost );
}
return costAndSell[year].cost;
}
int minCost( int i1, int i2, int i3 ){
if ( (i1<=i2) && (i1<=i3) ){
return(0);
}else if ( (i2<=i1) && (i2<=i3) ){
return(1);
}else if ( (i3<=i1) && (i3<=i2) ){
return(2);
}
}
void findBestPath( int lastYear ){
int i;
int rtn;
int sellYear;
for( i=1; i<=lastYear; i++ ){
rtn=minCost( sellAt(i-1)+sellCost[0], sellAt(i-2)+sellCost[1], sellAt(i-3)+sellCost[2]);
switch (rtn){
case 0:
costAndSell[i].cost=costAndSell[i-1].cost+sellCost[0];
costAndSell[i].lastSellYear=i-1;
break;
case 1:
costAndSell[i].cost=costAndSell[i-2].cost+sellCost[1];
costAndSell[i].lastSellYear=i-2;
break;
case 2:
costAndSell[i].cost=costAndSell[i-3].cost+sellCost[2];
costAndSell[i].lastSellYear=i-3;
break;
}
}
sellYear=costAndSell[lastYear].lastSellYear;
printf("sellAt[%d], cost[%d]\n", lastYear, costAndSell[lastYear].cost );
do{
sellYear=costAndSell[sellYear].lastSellYear;
printf("sellAt[%d], cost[%d]\n", sellYear, costAndSell[sellYear].cost );
} while( sellYear>0 );
}
void main(int argc, char * argv[]){
int lastYear;
initSellCost();
lastYear=atoi(argv[1]);
findBestPath(lastYear);
}
The output is :
sellAt[5], cost[228000]
sellAt[3], cost[176000]
sellAt[0], cost[100000]

I think it can't use dynamic programming because I can't find optimal substructure and overlapping subproblems for it.
The cost of year N depends on the behavior of year N-1, N-2 and N-3 . It is difficult to find a optimal substructure.

The link you provided is dynamic programming - and the code is pretty easy to read. I'd recommend taking a good look at the code to see what it is doing.

The following method might work.
At the end of each year , you have two choices. You can either Choose to Trade or choose to Pay the maintenance cost for another year. Except for the 3rd year, where you dont have a choice. You have to trade for sure.
This can be solved by a recursion method where you choose the minimum cost among trading and not trading during a particular year.
A Table can be maintained for offset, nth year (without trading) so that values need not be recalculated

Related

Classical bottom-up iterative approach for 0-1 Knapsack variation

I am not good at DP and trying to improve with practice over time. I was trying to solve this problem.
Given prices and favor-value of n items, determine the max value that
you can buy for a pocket money of m. The twist here is that once you
spend above $2000, you can get refund of $200, so effectively your
purchasing power becomes m+200.
I could solve this using recursive approach, but I am more curious to solve it with classical iterative 0-1 Knapsack bottom-up approach as given below, by some modification :
int Knapsack(vector<int> wt, vector<int> val, int cap)
{
int i,j;
int n = wt.size();
for(i=0;i<=n;i++)
t[i][0] = 0;
for(j=0;j<=cap;j++) //cap is the max allowed weight of knapsack
t[0][j] = 0;
for(i=1;i<=n;i++)
{
for(j=1;j<=cap;j++)
{
if(wt[i-1]<=j)
t[i][j] = max(val[i-1]+t[i-1][j-wt[]i-1], t[i-1][j]);
else
t[i][j] = t[i-1][j];
}
}
return t[n][cap];
}
In the above, the wt[] can actually become the price of each item and favor-value is val[]. The cap needs to be modified to cap+200 but with some tricky checks which I couldn't figure out after trying for 2 days now. What modifications can I do here to take extra $200 for shopping above $2000. I tried many approaches but failed. I tried searching on internet but mostly there are recursive approaches used which I have already figured out or they have use space saving trick by using 1-D array. But I want to modify the classical solution to solve it, so as to improve and verify my understanding. Can some please give me a hint or direction.

An algorithm to calculate foreign residence without enumerating days?

The visa conditions for a country to which I travel frequently include this restriction:
"You may reside in [country] for a maximum of 90 days in any period of 180"
Given a tentative list of pairs of dates (entry and exit dates), is there an algorithm that can tell me, for each visit, whether I will be in or out of compliance, and by how many days?
Clearly, one way to do this would be to build a large array of individual days and then slide a 180-day window along it, counting residence days. But I'm wondering whether there is a more elegant method which doesn't involve building a great long list of days.
The normal algorithm for this is basically a greedy algorithm, though it could also be seen as a 1D dynamic progamming algorithm. Basically, rather than sliding the window 1 day at a time, you slide it 1 starting-date at a time. Like so:
first_interval = 0
last_interval = 0
for first_interval = 0 to N:
# include more intervals as long as they (partially) fit within 180 days after the first one
while last_interval < N and A[last_interval].start - A[first_interval].start < 180:
last_interval += 1
calculate total number of days in intervals, possibly clipping the last one
The need to clip the last interval makes it a bit less elegant than it could otherwise be: in similar algorithms, rather than summing the total each time, you add to it for added-on intervals (when incrementing last_interval) and subtract from it for left-behind intervals (when incrementing first_interval). You could do something kind of similar here with the second-to-last interval, but unless you're in a serious performance bind it's probably best not to.
The following C++ code calculates the duration between two arbitrary dates no earlier than Jan 1, 1 A.D. in O(1) time. Is this what you're looking for?
#include <iostream>
using namespace std;
int days(int y,int m,int d){
int i,s,n[13]={0,31,28,31,30,31,30,31,31,30,31,30,31};
y+=(m-1)/12; m=(m-1)%12+1; if(m<1) {y--; m+=12;}
y--; s=y*365+y/4-y/100+y/400; y++;
if(y%4==0 && y%100 || y%400==0) n[2]++;
for(i=1;i<m;i++) s+=n[i]; s+=d; return s;
}
int main(){
cout<<days(2017,8,14)-days(2005,2,28)<<endl;
return 0;
}
You can use the days() function to map all dates of entry and exit to integers and then use the Sneftel's algorithm.

How to optimize scoring in a match

I'm designing a decision making system for a contest, where players are required to aim different targets. The probabilities to score on different targets vary, and the more players score on each target, the probability to score on that one decreases. Players have limited attempt chances.
What I come to mind is only Markov Chain and game theory yet I don't know how to implement them, and I wonder is there any other mathematical techniques to maximize my score.
I would deeply appreciate any piece of guidance.
Markov Processes: A Non-Solution
I don't think a Markov process is going to work here. The Markov property requires that the probability distribution of a process's future states depend only on its present state (or a limited number of past st
A stochastic process has the Markov property if the conditional probability distribution of future states of the process (conditional on both past and present states) depends only upon the present state, not on the sequence of events that preceded it. Since the probability of hitting a target decreases with each successful hit, the future of your process depends on its past and, therefore, the your process is not Markov.
Recursive Brute-Force Search: An Adequate Solution
One way to solve this is via exploration of a search tree. The following C++ code describes the operation:
#include <limits>
#include <iostream>
#include <cstdio>
#include <vector>
std::vector<float> ScoreOn(const std::vector<float> &probs, int target){
std::vector<float> temp = probs; //Copy original array to avoid corrupting it
temp[target] *= 0.9; //Decrease the probability
return temp; //Return the new array
}
std::pair<float,int> Choice(
const std::vector<float> &probs,
const std::vector<float> &values,
int depth
){
if(depth==0) //We gotta cut this off somewhere
return std::make_pair(0.0,-1); //Return 0 value at the end of time
//Any real choice will have a value greater than this
float valmax = -std::numeric_limits<float>::infinity();
//Will shortly be filled with a real choice value
int choice = -1;
//Loop through all targets
for(int t=0;t<probs.size();t++){
float hit_value = values[t]+Choice(ScoreOn(probs,t),values,depth-1).first;
float miss_value = 0 +Choice(probs ,values,depth-1).first;
float val = probs[t]*hit_value+(1-probs[t])*miss_value;
if(val>valmax){ //Is current target a better choice?
valmax = val;
choice = t;
}
}
return std::make_pair(valmax,choice);
}
int main(){
//Generate sample data and print the current best answer
int target_count = 8; //Number of targets
std::vector<float> probs,values;
for(int t=0;t<target_count;t++){
probs.push_back(rand()/(float)RAND_MAX);
values.push_back(80.0*(rand()/(float)RAND_MAX));
}
std::cout<<Choice(probs,values,6).first<<std::endl;
}
Now, consider trying to hit a target. If we hit it, the value of our action (call it H) is the value of the target plus the value of all of our future actions. If we miss (M), then the value is zero plus the value of all of our future actions. We weight these values by the probability p of each happening, to get the equation:
Value = pH+(1-p)M
We calculate the future values in the same way, thereby generating a recursive function. Since this could go on forever, we bound the depth of the recursion to some number of levels. Since, at each level the decision tree splits along two paths for each of t targets, we have (2t)**(Depth+1)-1 nodes in the tree. Therefore, choose your depth wisely to avoid thinking forever.
The above code, with optimizations runs in 0.044s for depth=5 and 0.557s for depth=6 on my Intel i5 M480 cpu (which is now about five years old). For depth=6, there are 268,435,455 nodes in the tree and each leaf possibility has only a 1 in 16,777,216 chance of being realized. Unless your value function is weird, there's no need to consider the future any farther than that.
Branch and Bound: An Improved Solution
But, if you did need to go explore a larger space or go faster, you could consider using Branch and Bound methods. This works the same way, except we choose not to expand any subtrees which are provably less than a solution we've already found. Proving a tight upper bound then becomes the primary challenge.
Why not use a greedy algorithm?
You can't do better at each point in time than to choose the target with the highest expected value (probability of hit multiplied by value of target).

How to find the minimum value of M?

I'm trying to solve this problem:
You have N relatives. You will talk to ith relative for exactly Ti
minutes. Each minute costs you 1 dollar . After the conversation,
they will add a recharge of Xi dollars in your mobile. Initially, you
have M dollars balance in your mobile phone.
Find the minimum value of M, that you must have initially, in your
phone, so that you don't run out of balance during any of the call
(encounter negative balance).
Note : You can call relatives in any order. Each relative will be
called exactly once.
Input:
N
T1 X1
T2 X2
2
1 1
2 1
Output:
2
This looks easy to me at first but I'm not able to find the exact solution.
My Initial thoughts:
We have no problem where Xi > Ti as it will not reduce our initial
balance. We need to take care of situation where where we will run
into loss i.e Ti > Xi.
But I am unable to make expression which will result in minimum
initial value.
Need guidance in approaching this problem to find optimal solution.
UPDATE:-
Binary Search approach seems to lead to wrong result (as proved by the
test case provided in the comment below by user greybeard.
So, this is another approach.We maintain the difference between call cost
and recharge amount.
Then we maintain two arrays/vectors.
If our recharge amount is strictly greater than cost of call, we put
the call in the first array ,else we put it in the second array.
Then we can sort the first array according to the cost and the second array
according to the recharge amount. We then update the diff by adding the
least amount of recharge from the call where our cost is greater than recharge
Then we can iterate through our first array and update our max
requirement,requirement for each call and current balance.Finally, our answer
will be the maximum between max requirement and the diff we have maintained.
Example :-
N = 2
T1 = 1 R1 = 1
T2 = 2 R2 = 1
Our first array contains nothing as all the calls have cost greater than
or equal to recharge amount. So, we place both calls in our second array
The diff gets updated to 2 before we sort the array. Then, we add the min
recharge we can get from the calls to our diff(i.e 1).Now, the diff stands
at 3.Then as our first array contains no elements, our answer is equal to
the diff i.e 3.
Time Complexity :- O(nlogn)
Working Example:-
#include<bits/stdc++.h>
using namespace std;
#define MAXN 100007
int n,diff;
vector<pair<int,int> > v1,v2;
int main(){
diff = 0;
cin>>n;
for(int i=0;i<n;i++){
int cost,recharge;
cin>>cost>>recharge;
if(recharge > cost){
v1.push_back(make_pair(cost,recharge));
}else{
v2.push_back(make_pair(recharge,cost));
}
diff += (cost-recharge);
}
sort(v1.begin(), v1.end());
sort(v2.begin(), v2.end());
if(v2.size() > 0)diff += v2[0].first;
int max_req = diff, req = 0,cur = 0;
for(int i=0; i<v1.size(); i++){
req = v1[i].first - cur;
max_req = max(max_req, req);
cur += v1[i].second-v1[i].first;
}
cout<<max(max_req,diff)<<endl;
return 0;
}
(This is a wiki post: you are invited to edit, and don't need much reputation to do so without involving a moderator.)
Working efficiently means accomplishing the task at hand, with no undue effort. Aspects here:
the OP asks for guidance in approaching this problem to find optimal solution - not for a solution (as this entirely similar, older question does).
the problem statement asks for the minimum value of M - not an optimal order of calls or how to find that.
To find the minimum balance initially required, categorise the relatives/(T, X)-pairs/calls (the order might have a meaning, if not for the problem as stated)
T < X Leaves X-T more for calls to follow. Do in order of increasing cost.
Start assuming an initial balance of 1. For each call, if you can afford it, subtract its cost, add its refund and be done accounting for it. If you can't afford it (yet), put it on hold/the back burner/in a priority queue. At the end of "rewarding calls", remove each head of the queue in turn, accounting for necassary increases in intitial balance.
This part ends with a highest balance, yet.
T = X No influence on any other call. Just do at top balance, in any order.
The top balance required for the whole sequence can't be lower than the cost of any single call, including these.
T > X Leaves T-X less for subsequent calls. Do in order of decreasing refund.
(This may, as any call, go to a balance of zero before refund.
As order of calls does not change the total cost, the ones requiring the least initial balance will be those yielding the lowest final one. For the intermediate balance required by this category, don't forget that least refund.)
Combine the requirements from all categories.
Remember the request for guidance.

Algorithm interview from Google

I am a long time lurker, and just had an interview with Google where they asked me this question:
Various artists want to perform at the Royal Albert Hall and you are responsible for scheduling
their concerts. Requests for performing at the Hall are accommodated on a first come first served
policy. Only one performance is possible per day and, moreover, there cannot be any concerts
taking place within 5 days of each other
Given a requested time d which is impossible (i.e. within 5 days of an already sched-
uled performance), give an O(log n)-time algorithm to find the next available day d2
(d2 > d).
I had no clue how to solve it, and now that the interview is over, I am dying to figure out how to solve it. Knowing how smart most of you folks are, I was wondering if you can give me a hand here. This is NOT for homework, or anything of that sort. I just want to learn how to solve it for future interviews. I tried asking follow up questions but he said that is all I can tell you.
You need a normal binary search tree of intervals of available dates. Just search for the interval containing d. If it does not exist, take the interval next (in-order) to the point where the search stopped.
Note: contiguous intervals must be fused together in a single node. For example: the available-dates intervals {2 - 15} and {16 - 23} should become {2 - 23}. This might happen if a concert reservation was cancelled.
Alternatively, a tree of non-available dates can be used instead, provided that contiguous non-available intervals are fused together.
Store the scheduled concerts in a binary search tree and find a feasible solution by doing a binary search.
Something like this:
FindDateAfter(tree, x):
n = tree.root
if n.date < x
n = FindDateAfter(n.right, x)
else if n.date > x and n.left.date < x
return n
return FindDateAfter(n.left, x)
FindGoodDay(tree, x):
n = FindDateAfter(tree, x)
while (n.date + 10 < n.right.date)
n = FindDateAfter(n, n.date + 5)
return n.date + 5
I've used a binary search tree (BST) that holds the ranges for valid free days that can be scheduled for performances.
One of the ranges must end with int.MaxValue, because we have an infinite amount of days so it can't be bound.
The following code searches for the closest day to the requested day, and returns it.
The time complexity is O(H) when H is the tree height (usually H=log(N), but can become H=N in some cases.).
The space complexity is the same as the time complexity.
public static int FindConcertTime(TreeNode<Tuple<int, int>> node, int reqDay)
{
// Not found!
if (node == null)
{
return -1;
}
Tuple<int, int> currRange = node.Value;
// Found range.
if (currRange.Item1 <= reqDay &&
currRange.Item2 >= reqDay)
{
// Return requested day.
return reqDay;
}
// Go left.
else if (currRange.Item1 > reqDay)
{
int suggestedDay = FindConcertTime(node.Left, reqDay);
// Didn't find appropriate range in left nodes, or found day
// is further than current option.
if (suggestedDay == -1 || suggestedDay > currRange.Item1)
{
// Return current option.
return currRange.Item1;
}
else
{
// Return suggested day.
return suggestedDay;
}
}
// Go right.
// Will always find because the right-most node has "int.MaxValue" as Item2.
else //if (currRange.Item2 < reqDay)
{
return FindConcertTime(node.Right, reqDay);
}
}
Store the number of used nights per year, quarter, and month. To find a free night, find the first year that is not fully booked, then the quarter within that year, then the month. Then check each of the nights in that month.
Irregularities in the calendar system makes this a little tricky so instead of using years and months you can apply the idea for units of 4 nights as "month", 16 nights as "quarter", and so on.
Assume, at level 1 all schedule details are available.
Group schedule of 16 days schedule at level 2.
Group 16 level 2 status at level 3.
Group 16 level 3 status at level 4.
Depends on number of days that you want to expand, increase the level.
Now search from higher level and do binary search at the end.
Asymtotic complexity:-
It means runtime is changing as the input grows.
suppose we have an input string “abcd”. Here we traverse through each character to find its length thus the time taken is proportional to the no of characters in the string like n no of char. Thus O(n).
but if we put the length of the string “abcd” in a variable then no matter how long the string be we still can find the length of thestring by looking at the variable len. (len=4).
ex: return 23. no matter what you input is we still have the output as 23.
thus the complexity is O(1). Thus th program will be running in a constant time wrt input size.
for O(log n) - the operations are happening in logarithmic steps.
https://drive.google.com/file/d/0B7eUOnXKVyeERzdPUE8wYWFQZlk/view?usp=sharing
Observe the image in the above link. Over here we can see the bended line(logarithmic line). Here we can say that for smaller inputs the O(log n) notation works good as the time taken is less as we can see in the bended line but when the input grows the linear notation i.e O(n) is considered as better way.
There are also the best and worst case scenarios to be seen. Like the above example.
You can also refer to this cheat for the algorithms: http://bigocheatsheet.com/
It was already mentioned above, but basically keep it simple with a binary tree. You know a binary tree has log N complexity. So you already know what algorithm you need to use.
All you have to do is to come up with a tree node structure and use binary tree insertion algorithm to find next available date:
A possible one:
The tree node has two attributes: d (date of the concert) and d+5 (end date for the blocking period of 5 days). Again to keep it simple, use a timestamp for the two date attributes.
Now it is trivial to find next available date by using binary tree inorder insertion algorithm with initial condition of root = null.
Why not try to use Union-Find? You can group each concert day + the next 5 days as part of one set and then perform a FIND on the given day which would return the next set ID which would be your next concert date.
If implemented using a tree, this gives a O(log n) time complexity.

Resources