Amazon Interview- Design Meeting Scheduler [closed] - algorithm

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
Lately, I took an interview. I was asked to design a meeting scheduler, just like in the Microsoft outlook calendar or the gmail calendar.
I proposed that I will create an array of 48 for each day. Every 30 min representing the array entry.
I have to make sure that the next appointment does not collide with a previous meeting.
My solution works fine but it wastes too much memory.
Can anyone please tell me how do I find a better solution to detect collision for meetings.
I don't know all the meetings at the beginning. They will be added randomly later.
Thanks,

Start with an empty list of meetings, each with a start_time and duration. We will maintain a sorted list of meetings by start_time.
To add a meeting to the list, find where it belongs in the list by performing a binary search. Once you find the index, perform two checks to avoid a collision; consider the meetings immediately before and after the to-be-inserted meeting (if they exist).
Assert the before-meeting's start_time + duration does not exceed the new meeting's start_time.
Assert the new meeting's start_time+duration does not exceed the after-meeting's start_time.
If the assertions are satisfied, add the meeting to the list.
This add operation takes O(log(list_size)) time.
Note: This approach assumes that adding a meeting with an overlap is an invalid operation. If overlaps are allowed to exist, you would have to check beyond the meetings immediately preceding/subsequent the new meeting.

We can have a Tree structure (BST) for storing the requests (Request object: start time/end time/date/priority etc.). By doing so, add/delete/search/update operations can be achieved by O(height_of_tree). If we use a balanced tree, we can get the optimized running time. i.e. O(log n) for each of the above mentioned operations.
This approach is better than the sorted list approach as the list is backed by an fixed sized array in case of ArrayList which takes O(n) for copying the elements from old array to new array. If we use a linkedlist, binary search is not possible.
Comments welcome!

Here is my solution which inserts using binary search
public class MeetingScheduler {
static class Meeting implements Comparable<Meeting> {
Date startTime;
Date endTime;
int duration;
public static final int MINUTE = 60000;
//duration in minutes
Meeting(Date startTime, int duration) {
this.startTime = startTime;
this.duration = duration;
this.endTime = new Date(startTime.getTime() + (MINUTE * duration));
}
#Override
public int compareTo(Meeting o) {
if (this.endTime.compareTo(o.startTime) < 0) {
return -1;
}//end time is before the other's start time
if (this.startTime.compareTo(o.endTime) > 0) {
return 1;
}////start time is after the other's end time
return 0;
}
#Override
public String toString() {
return "meeting {" +
"from " + startTime +
", minutes=" + duration +
'}';
}
}
private List<Meeting> meetings = new ArrayList<Meeting>();
public Meeting bookRoom(Meeting meeting) {
if (meetings.isEmpty()) {
meetings.add(meeting);
return null;
} else {
int pos = -Collections.binarySearch(meetings, meeting);
if (pos > 0) {
meetings.add(pos-1, meeting);
return null;
} else {
return meetings.get(-pos);
}
}
}
public List<Meeting> getMeetings() {
return meetings;
}
public static void main(String[] args) {
MeetingScheduler meetingScheduler = new MeetingScheduler();
Meeting[] meetingsToBook = new Meeting[]{
//October 3rd 2014
new Meeting(new Date(2014 - 1900, 10 - 1, 3, 15, 00), 15),
new Meeting(new Date(2014 - 1900, 10 - 1, 3, 16, 00), 15),
new Meeting(new Date(2014 - 1900, 10 - 1, 3, 17, 00), 60),
new Meeting(new Date(2014 - 1900, 10 - 1, 3, 18, 00), 15),
new Meeting(new Date(2014 - 1900, 10 - 1, 3, 14, 50), 10),
new Meeting(new Date(2014 - 1900, 10 - 1, 3, 14, 55), 10)
};
for (Meeting m : meetingsToBook) {
Meeting oldMeeting = meetingScheduler.bookRoom(m);
if (oldMeeting != null) {
System.out.println("Could not book room for " + m + " because it collides with " + oldMeeting);
}
}
System.out.println("meetings booked: " + meetingScheduler.getMeetings().size());
for (Meeting m : meetingScheduler.getMeetings()) {
System.out.println(m.startTime + "-> " + m.duration + " mins");
}
}
}

while the use of a sorted array and binary search is efficient, please note that insert will take o(n) assuming no collision is found since the array needs to slide the meetings over. Not sure if this is the most optimal solution.

If the sorted list is an array, I believe the add operation will take O(n) since you have to shift the meeting that start after the to-be-insert meeting.

Related

Least number of days required to finish watching all movies given durations array if you can watch maximum 3.00 duration movie per day

Input: double array representing duration of movies e.g.
durations[] ={1.01, 2.4, 1.01, 1.01, 1.4}. You can watch maximum
3.00 duration movie per day.
Find the least number of days needed to finish watching all the movies.
Constraint: 1.01 <= duration[i]
<= 3.00.
(You can choose to watch any movie on a day and won't repeat
watching a movie)
Sample Test Cases:
Input: duration[] = {1.01, 2.4, 1.01, 1.01,
1.4} Output: 3
Input: duration[] = {1.01, 2.4, 1.4, 1.6, 2.6, 1.7} Output: 4
Input: duration[] = {1.01, 2.4, 1.5, 1.6, 2.6, 1.7} Output: 5
I got this in a placement coding test and couldn't finish it on time
but did it later using recursion. It worked with few test cases I
custom made but I'm not not sure if it will work for all possible
test cases. Also I feel it could be enhanced for better time
complexity. Kindly help.
My insight: You would be able to watch max 2 movies a day as
durations are always >= 1.01 so watching any 3 movies would make
duration exceed 3.00.
Here's my code:
import java.util.ArrayList;
public class MoviesBetterSolution {
public static void main(String[] args) {
double arr[] = {2.0,1.01,1.4,2.4,1.71}; //test case
System.out.println( f( 0, 0.00 , 1, 3.00, new ArrayList<Integer>(), arr , 0) );
//days passed a 1 as we start from day 1
//initial wtn (watched till now for a particular day) passes is 0.00
} static int minDays = Integer.MAX_VALUE;
//wtn -> watched till now (keeps track of duration of movies watched on the current day
//taken keeps track of number of movies watched on current day
// picked : watched movies on the day till now private static int f(int i, double wtn, int days, double limit, ArrayList<Integer>
picked, double[] arr, int taken) {
//updating minDays after reaching a point where all movies have been watched
if(picked.size()==arr.length) {
if( days<minDays ) minDays = days;
return minDays;
}
if(i == arr.length) { //finished traversing array
if(taken != 0) { //restart traversing to watch unwatched movies only if atleast 1
//movie was watched on the day, setting taken for the new traversal to be 0
i = 0;
taken = 0; }else { // otherwise just return if nothing was watched on the day, otherwise it
//will stackoverflow for all non watch choice recursion branch
return minDays;` } }
if((wtn + arr[i] <= limit) && !(picked.contains(i)) ) { //only movies that havent been watched can be watched
ArrayList<Integer> temp = new ArrayList<Integer>();
temp = (ArrayList<Integer>) picked.clone();
temp.add(i);
if(taken<2) { //as u can watch only 2 movies a day
f(i+1, wtn + arr[i] , days, limit, temp, arr, taken+1); //watch & move to next movie but on same day }
f(0, 0 , days +1 , limit, temp, arr, taken+1); // watch & move to next movie but on next day , wtn and index(i) set to 0 as u
starting new day }
f(i+1, wtn, days, limit, picked, arr, taken); //not watch & move to next movie on same day
return minDays; } }
Assuming all movies have run times between 1.01 and 3.00, solve this in O(n log n)
1. sort your list
2. set days = 0
3. set two pointers to the two ends of your list.
4. repeatedly do the following until all elements have been processed:
4.1 increment days
4.2 if the sum of the movies referred to is <= 3.0 then move both pointers towards the center, otherwise just decrement the larger one.
Just before the end, it's possible that both pointers refer to the same element. That takes a day.

Algorithm for distributing beads puzzle (2)?

Let's say you have a circle (shown below) with N slots.
Your goal is to end up with a specified number of beads in each slot, and you have an array of size N containing the amount of beads you need in each slot. For example, if the array was {1, 5, 3}, then you would need to end up with 1 bead in slot 1, 5 beads in slot 2, and 3 beads in slot 3. You have an infinite amount of beads.
You can "unlock" X slots. Once you unlock a slot, you can start putting beads in that slot. You can move beads that are already in slots, but you can only move clockwise.
What is the minimum distance the beads have to move in order to solve the problem?
Here's an example:
N = 6, X = 2. Array: {2, 5, 4, 2, 6, 2}
Unlock slots 2 and 5. Put 11 bead into slot 2 and travel a total distance of 8 to get to slots 2, 3, and 4. Put 10 beads into slot 5 and travel a total distance of 6 to get to slots 5, 6 and 1. 8 + 6 = 14, so the answer is 14.
The problem has some things to note:
there is no benefit in moving beads to (or beyond) another unlocked slot, as the number of moves would be smaller if those beads started in that other unlocked slot;
as a consequence, once the slots to unlock are chosen, the amounts of beads to put in those, and the number of moves is determined;
if the cost (number of moves) has been calculated for a particular set of unlocked slots, then the cost for a neighbouring configuration (where a previously unlocking slot remains locked, but the next slot gets unlocked) can be easily derived, without having to calculate from scratch;
it is not true that the slot that must receive the most beans is always unlocked in the optimal solution;
it is not true that if one slot choice is kept variable that the cost will either increase or decrease in the direction that the next slot is chosen; it can go up and down, up again and down again.
The algorithm suggested here will go through all combinations of possible slot selections and select the combination with the lowest moves. The time complexity is thus O(n!/[(n-x)!x!]).
I think that there should be a more efficient algorithm, which does not need to visit all combinations, but I didn't find any mathematical pattern that would allow this.
Here is the algorithm in a JavaScript snippet:
function optimalCollectors(beadCounts, collectorCount) {
// Initialisation
var n = beadCounts.length;
if (n < collectorCount) return {error: "not enough slots"};
var beads = beadCounts.reduce(function (beads, beadCount) {
return beads + beadCount;
});
var cost = beadCounts.reduce(function (cost, beadCount, idx) {
return cost + beadCount * (idx+1);
});
var solution = {
cost: cost, // too large, to make sure it gets improved
slots: [] // numbers of the slots chosen for the solution
};
var collectorSlots = Array(collectorCount);
function findNextCollector(collectorNo, startSlot, cost, beads) {
var addBeads = 0;
for (var slot=startSlot; slot<=n-collectorCount+collectorNo; slot++) {
collectorSlots[collectorNo] = slot;
// progressively calculate total cost, and number of beads
// "in front" of the currently tried slot.
cost -= beads;
beads += addBeads - beadCounts[slot];
if (collectorNo == collectorCount - 1) { // all slots chosen
if (cost < solution.cost) { // found a current best
solution.cost = cost;
// copy currently selected slot numbers:
solution.slots = collectorSlots.slice(0);
}
} else {
findNextCollector(collectorNo+1, slot+1, cost, beads);
}
if (collectorNo) {
cost += beadCounts[slot] * (slot + 1 - startSlot);
} else {
cost += beadCounts[slot] * (n - 1);
addBeads = beadCounts[slot];
}
}
}
findNextCollector(0, 0, cost, beads);
return solution;
}
function randomInput(n) {
// The random values are in the range 0..n-1. This is just
// a convenient choice, in reality there has not to be a limit.
return Array.from({length: n}, x => Math.floor(Math.random() * n));
}
// Link with I/O
var beads = document.getElementById('beads');
var collectors = document.getElementById('collectors');
var randomize = document.getElementById('randomize');
var calculate = document.getElementById('calculate');
var output = document.getElementById('output');
// Capture events
randomize.onclick = function() {
var n = 5 + Math.floor(Math.random() * 7);
beads.value = randomInput(n).join(',');
collectors.value = 2 + Math.floor(Math.random() * (n/2-2));
calculate.onclick();
};
calculate.onclick = function() {
var beadCounts = beads.value.split(',').map(Number);
var collectorCount = Number(collectors.value);
var solution = optimalCollectors(beadCounts, collectorCount);
if (solution.error) {
output.textContent = 'Error: ' + solution.error;
return;
}
output.textContent =
'\nInput: ' + JSON.stringify(beadCounts) +
'\nNumber of moves: ' + solution.cost +
'\nChosen slots (0-based): ' + JSON.stringify(solution.slots);
};
Comma-separated list of number of beads per slot:<br/>
<input id="beads" size="30" value="2, 5, 4, 2, 6, 2">
<button id="randomize">Randomize</button><br/>
Number of bead-collecting slots:<br/>
<input id="collectors" size="3" value="2"></br>
<button id="calculate">Find collector slots minimising cost</button></br>
<pre id="output"></pre>

Quiz scoring algorithm for how close an answer is

I am trying to generate a small algorithm that will give a user a decimal score out of 1 based on how close their answer is to a true answer. These answers will always be numeric and be things like 'How many x did this?'
I will be setting a sensible maximum and minimum value for each answer where if a users answer exceeds this, they will score nothing though am a bit stuck on getting an equation created ...
As an example, a correct answer could be 100 and a sensible minimum could be set as 50. A user specifying 75 would thus be given a score of 0.5
Perhaps getting a bit complicated now but it would also be nice to allocate the score on a curve so the result is not linear and thus weighting is higher the nearer you are to the correct answer
Any help or better ideas for this scoring would be much appreciated
A formula code could be like this :
score = abs(input - answer) / (answer - min)
for your example we have input = 75 , answer = 100 and min = 50 so:
score = abs(75 - 100) / (100 - 50) = 25 / 50 = 0.5
If you wanted the scoring to be non-linear (to reward closeness to the answer) you could try a 'squared difference' formula. E.g.
score = 1 - (abs((answer - input)/(answer - minimum)))^2
e.g. with correct = 100, minimum = 60, answer = 70 you would get:
score = 1 - (abs((100 - 70)/(100 - 60)))^2 = 0.4375
If you want to give a greater reward for closeness, you could use a higher power. Note that division by zero will occur if answer = minimum.
I implemented the algorithm in Java and made a small test case.
public class Quiz{
public static double calculateScore(int input,
int correctAnswer,
int minimumAnswer){
if(input == correctAnswer){
return 1;
}
double correctInterval = Math.abs(correctAnswer - minimumAnswer);
double relativeAnswer = Math.abs(correctAnswer - input);
if(relativeAnswer > correctInterval){
return 0;
}else{
double score = relativeAnswer/correctInterval;
score *= score;// make ^2 to avoid a linear progression
return 1.0 - score;
}
}
}
public class QuizTest{
#Test
public void testCalculateScore() {
assertTrue(0 == Quiz.calculateScore(5, 20, 15));
assertTrue(0 == Quiz.calculateScore(30, 20, 15));
assertTrue(1 == Quiz.calculateScore(20, 20, 15));
assertTrue(0 < Quiz.calculateScore(17, 20, 15));
assertTrue(0 < Quiz.calculateScore(22, 20, 15));
assertTrue(Quiz.calculateScore(18, 20, 15) == Quiz.calculateScore(22, 20, 15));
assertTrue(Quiz.calculateScore(17, 20, 15) < Quiz.calculateScore(22, 20, 15));
}
}
The test run is successful

Knapsack: how to add item type to existing solution

I've been working with this variation of dynamic programming to solve a knapsack problem:
KnapsackItem = Struct.new(:name, :cost, :value)
KnapsackProblem = Struct.new(:items, :max_cost)
def dynamic_programming_knapsack(problem)
num_items = problem.items.size
items = problem.items
max_cost = problem.max_cost
cost_matrix = zeros(num_items, max_cost+1)
num_items.times do |i|
(max_cost + 1).times do |j|
if(items[i].cost > j)
cost_matrix[i][j] = cost_matrix[i-1][j]
else
cost_matrix[i][j] = [cost_matrix[i-1][j], items[i].value + cost_matrix[i-1][j-items[i].cost]].max
end
end
end
cost_matrix
end
def get_used_items(problem, cost_matrix)
i = cost_matrix.size - 1
currentCost = cost_matrix[0].size - 1
marked = Array.new(cost_matrix.size, 0)
while(i >= 0 && currentCost >= 0)
if(i == 0 && cost_matrix[i][currentCost] > 0 ) || (cost_matrix[i][currentCost] != cost_matrix[i-1][currentCost])
marked[i] = 1
currentCost -= problem.items[i].cost
end
i -= 1
end
marked
end
This has worked great for the structure above where you simply provide a name, cost and value. Items can be created like the following:
items = [
KnapsackItem.new('david lee', 8000, 30) ,
KnapsackItem.new('kevin love', 12000, 50),
KnapsackItem.new('kemba walker', 7300, 10),
KnapsackItem.new('jrue holiday', 12300, 30),
KnapsackItem.new('stephen curry', 10300, 80),
KnapsackItem.new('lebron james', 5300, 90),
KnapsackItem.new('kevin durant', 2300, 30),
KnapsackItem.new('russell westbrook', 9300, 30),
KnapsackItem.new('kevin martin', 8300, 15),
KnapsackItem.new('steve nash', 4300, 15),
KnapsackItem.new('kyle lowry', 6300, 20),
KnapsackItem.new('monta ellis', 8300, 30),
KnapsackItem.new('dirk nowitzki', 7300, 25),
KnapsackItem.new('david lee', 9500, 35),
KnapsackItem.new('klay thompson', 6800, 28)
]
problem = KnapsackProblem.new(items, 65000)
Now, the problem I'm having is that I need to add a position for each of these players and I have to let the knapsack algorithm know that it still needs to maximize value across all players, except there is a new restriction and that restriction is each player has a position and each position can only be selected a certain amount of times. Some positions can be selected twice, others once. Items would ideally become this:
KnapsackItem = Struct.new(:name, :cost, :position, :value)
Positions would have a restriction such as the following:
PositionLimits = Struct.new(:position, :max)
Limits would be instantiated perhaps like the following:
limits = [Struct.new('PG', 2), Struct.new('C', 1), Struct.new('SF', 2), Struct.new('PF', 2), Struct.new('Util', 2)]
What makes this a little more tricky is every player can be in the Util position. If we want to disable the Util position, we will just set the 2 to 0.
Our original items array would look something like the following:
items = [
KnapsackItem.new('david lee', 'PF', 8000, 30) ,
KnapsackItem.new('kevin love', 'C', 12000, 50),
KnapsackItem.new('kemba walker', 'PG', 7300, 10),
... etc ...
]
How can position restrictions be added to the knapsack algorithm in order to still retain max value for the provided player pool provided?
There are some efficient libraries available in ruby which could suit your task , Its clear that you are looking for some constrain based optimization , there are some libraries in ruby which are a opensource so, free to use , Just include them in you project. All you need to do is generate Linear programming model objective function out of your constrains and library's optimizer would generate Solution which satisfy all your constrains , or says no solution exists if nothing can be concluded out of the given constrains .
Some such libraries available in ruby are
RGLPK
OPL
LP Solve
OPL follows the LP syntax similar to IBM CPLEX , which is widely used Optimization software, So you could get good references on how to model the LP using this , Moreover this is build on top of the RGLPK.
As I understand, the additional constraint that you are specifying is as following:
There shall be a set of elements, out which only at most k (k = 1 or
2) elements can be selected in the solution. There shall be multiple
such sets.
There are two approaches that come to my mind, neither of which are efficient enough.
Approach 1:
Divide the elements into groups of positions. So if there are 5 positions, then each element shall be assigned to one of 5 groups.
Iterate (or recur) through all the combinations by selecting 1 (or 2) element from each group and checking the total value and cost. There are ways in which you can fathom some combinations. For example, in a group if there are two elements in which one gives more value at lesser cost, then the other can be rejected from all solutions.
Approach 2:
Mixed Integer Linear Programming Approach.
Formulate the problem as follows:
Maximize summation (ViXi) {i = 1 to N}
where Vi is value and
Xi is a 1/0 variable denoting presence/absence of an element from the solution.
Subject to constraints:
summation (ciXi) <= C_MAX {total cost}
And for each group:
summation (Xj) <= 1 (or 2 depending on position)
All Xi = 0 or 1.
And then you will have to find a solver to solve the above MILP.
This problem is similar to a constraint vehicle routing problem. You can try a heuristic like the saving algorithm from Clarke&Wright. You can also try a brute-force algorithm with less players.
Considering players have Five positions your knapsack problem would be:-
Knpsk(W,N,PG,C,SF,PF,Util) = max(Knpsk(W-Cost[N],N-1,...)+Value[N],Knpsk(W,N-1,PG,C,SF,PF,Util),Knpsk(W-Cost[N],N-1,PG,C,SF,PF,Util-1)+Value[N])
if(Pos[N]=="PG") then Knpsk(W-Cost[N],N-1,....) = Knpsk(W-Cost[N],N-1,PG-1,....)
if(Pos[N]=="C") then Knpsk(W-Cost[N],N-1,....) = Knpsk(W-Cost[N],N-1,PG,C-1....)
so on...
PG,C,SF,PF,Util are current position capacities
W is current knapsack capacity
N number of items available
Dynamic Programming can be used as before using 7-D table and as in your case the values of positions are small it will slow down algorithm by factor of 16 which is great for n-p complete problem
Following is dynamic programming solution in JAVA:
public class KnapsackSolver {
HashMap CostMatrix;
// Maximum capacities for positions
int posCapacity[] = {2,1,2,2,2};
// Total positions
String[] positions = {"PG","C","SF","PF","util"};
ArrayList playerSet = new ArrayList<player>();
public ArrayList solutionSet;
public int bestCost;
class player {
int value;
int cost;
int pos;
String name;
public player(int value,int cost,int pos,String name) {
this.value = value;
this.cost = cost;
this.pos = pos;
this.name = name;
}
public String toString() {
return("'"+name+"'"+", "+value+", "+cost+", "+positions[pos]);
}
}
// Used to add player to list of available players
void additem(String name,int cost,int value,String pos) {
int i;
for(i=0;i<positions.length;i++) {
if(pos.equals(positions[i]))
break;
}
playerSet.add(new player(value,cost,i,name));
}
// Converts subproblem data to string for hashing
public String encode(int Capacity,int Totalitems,int[] positions) {
String Data = Capacity+","+Totalitems;
for(int i=0;i<positions.length;i++) {
Data = Data + "," + positions[i];
}
return(Data);
}
// Check if subproblem is in hash tables
int isDone(int capacity,int players,int[] positions) {
String k = encode(capacity,players,positions);
if(CostMatrix.containsKey(k)) {
//System.out.println("Key found: "+k+" "+(Integer)CostMatrix.get(k));
return((Integer)CostMatrix.get(k));
}
return(-1);
}
// Adds subproblem added hash table
void addEncode(int capacity,int players,int[] positions,int value) {
String k = encode(capacity,players,positions);
CostMatrix.put(k, value);
}
boolean checkvalid(int capacity,int players) {
return(!(capacity<1||players<0));
}
// Solve the Knapsack recursively with Hash look up
int solve(int capacity,int players,int[] posCapacity) {
// Check if sub problem is valid
if(checkvalid(capacity,players)) {
//System.out.println("Processing: "+encode(capacity,players,posCapacity));
player current = (player)playerSet.get(players);
int sum1 = 0,sum2 = 0,sum3 = 0;
int temp = isDone(capacity,players-1,posCapacity);
// Donot add player
if(temp>-1) {
sum1 = temp;
}
else sum1 = solve(capacity,players-1,posCapacity);
//check if current player can be added to knapsack
if(capacity>=current.cost) {
posCapacity[posCapacity.length-1]--;
temp = isDone(capacity-current.cost,players-1,posCapacity);
posCapacity[posCapacity.length-1]++;
// Add player to util
if(posCapacity[posCapacity.length-1]>0) {
if(temp>-1) {
sum2 = temp+current.value;
}
else {
posCapacity[posCapacity.length-1]--;
sum2 = solve(capacity-current.cost,players-1,posCapacity)+current.value;
posCapacity[posCapacity.length-1]++;
}
}
// Add player at its position
int i = current.pos;
if(posCapacity[i]>0) {
posCapacity[i]--;
temp = isDone(capacity-current.cost,players-1,posCapacity);
posCapacity[i]++;
if(temp>-1) {
sum3 = temp+current.value;
}
else {
posCapacity[i]--;
sum3 = solve(capacity-current.cost,players-1,posCapacity)+current.value;
posCapacity[i]++;
}
}
}
//System.out.println(sum1+ " "+ sum2+ " " + sum3 );
// Evaluate the maximum of all subproblem
int res = Math.max(Math.max(sum1,sum2), sum3);
//add current solution to Hash table
addEncode(capacity, players, posCapacity,res);
//System.out.println("Encoding: "+encode(capacity,players,posCapacity)+" Cost: "+res);
return(res);
}
return(0);
}
void getSolution(int capacity,int players,int[] posCapacity) {
if(players>=0) {
player curr = (player)playerSet.get(players);
int bestcost = isDone(capacity,players,posCapacity);
int sum1 = 0,sum2 = 0,sum3 = 0;
//System.out.println(encode(capacity,players-1,posCapacity)+" "+bestcost);
sum1 = isDone(capacity,players-1,posCapacity);
posCapacity[posCapacity.length-1]--;
sum2 = isDone(capacity-curr.cost,players-1,posCapacity) + curr.value;
posCapacity[posCapacity.length-1]++;
posCapacity[curr.pos]--;
sum3 = isDone(capacity-curr.cost,players-1,posCapacity) + curr.value;
posCapacity[curr.pos]++;
if(bestcost==0)
return;
// Check if player is not added
if(sum1==bestcost) {
getSolution(capacity,players-1,posCapacity);
}
// Check if player is added to util
else if(sum2==bestcost) {
solutionSet.add(curr);
//System.out.println(positions[posCapacity.length-1]+" added");
posCapacity[posCapacity.length-1]--;
getSolution(capacity-curr.cost,players-1,posCapacity);
posCapacity[posCapacity.length-1]++;
}
else {
solutionSet.add(curr);
//System.out.println(positions[curr.pos]+" added");
posCapacity[curr.pos]--;
getSolution(capacity-curr.cost,players-1,posCapacity);
posCapacity[curr.pos]++;
}
}
}
void getOptSet(int capacity) {
CostMatrix = new HashMap<String,Integer>();
bestCost = solve(capacity,playerSet.size()-1,posCapacity);
solutionSet = new ArrayList<player>();
getSolution(capacity, playerSet.size()-1, posCapacity);
}
public static void main(String[] args) {
KnapsackSolver ks = new KnapsackSolver();
ks.additem("david lee", 8000, 30, "PG");
ks.additem("kevin love", 12000, 50, "C");
ks.additem("kemba walker", 7300, 10, "SF");
ks.additem("jrue holiday", 12300, 30, "PF");
ks.additem("stephen curry", 10300, 80, "PG");
ks.additem("lebron james", 5300, 90, "PG");
ks.additem("kevin durant", 2300, 30, "C");
ks.additem("russell westbrook", 9300, 30, "SF");
ks.additem("kevin martin", 8300, 15, "PF");
ks.additem("steve nash", 4300, 15, "C");
ks.additem("kyle lowry", 6300, 20, "PG");
ks.additem("monta ellis", 8300, 30, "C");
ks.additem("dirk nowitzki", 7300, 25, "SF");
ks.additem("david lee", 9500, 35, "PF");
ks.additem("klay thompson", 6800, 28,"PG");
//System.out.println("Items added...");
// System.out.println(ks.playerSet);
int maxCost = 30000;
ks.getOptSet(maxCost);
System.out.println("Best Value: "+ks.bestCost);
System.out.println("Solution Set: "+ks.solutionSet);
}
}
Note: If players with certain positions are added more than its capacity then those added as util because players from any position can be added to util.

Merge ranges in intervals

Given a set of intervals: {1-4, 6-7, 10-12} add a new interval: (9,11) so that the final solution is 'merged': Output: {1-4, 6-7, 9-12}. The merger can happen on both sides (low as well as high range).
I saw this question was answered at multiple places, someone even suggested using Interval Tress, but did not explain how exactly they would use it. The only solution I know of is to arrange the intervals in ascending order of their start time and iterating over them and trying to merge them appropriately.
If someone can help me understand how we can use interval trees in this use case, that will be great!
[I have been following interval trees in CLRS book, but they do not talk about merging, all they talk about is insertion and search.]
(I'm assuming that this means that intervals can never overlap, since otherwise they'd be merged.)
One way to do this would be to store a balanced binary search tree with one node per endpoint of a range. Each node would then be marked as either an "open" node marking the start of an interval or a "close" node marking the end of an interval.
When inserting a new range, one of two cases will occur regarding the start point of the range:
It's already inside a range, which means that you will extend an already-existing range as part of the insertion.
It's not inside a range, so you'll be creating a new "open" node.
To determine which case you're in, you can do a predecessor search in the tree for the range's start point. If you get NULL or a close node, you need to insert a new open node representing the start point of the range. If you get an open node, you will just keep extending that interval.
From there, you need to determine how far the range extends. To do this, continuously compute the successor of the initial node you inserted until one of the following occurs:
You have looked at all nodes in the tree. In that case, you need to insert a close node marking the end of this interval.
You see a close node after the end of the range. In that case, you're in the middle of an existing range when the new range ends, so you don't need to do anything more. You're done.
You see a close or open node before the end of the range. In that case, you need to remove that node from the tree, since the old range is subsumed by the new one.
You see an open node after the end of the range. In that case, insert a new close node into the tree, since you need to terminate the current range before seeing the start of this new one.
Implemented naively, the runtime of this algorithm is O(log n + k log n), where n is the number of intervals and k is the number of intervals removed during this process (since you have to do n deletes). However, you can speed this up to O(log n) by using the following trick. Since the deletion process always deletes nodes in a sequence, you can use a successor search for the endpoint to determine the end of the range to remove. Then, you can splice the subrange to remove out of the tree by doing two tree split operations and one tree join operation. On a suitable balanced tree (red-black or splay, for example), this can be done in O(log n) total time, which is much faster if a lot of ranges are going to get subsumed.
Hope this helps!
public class MergeIntervals {
public static class Interval {
public double start;
public double end;
public Interval(double start, double end){
this.start = start;
this.end = end;
}
}
public static List<Interval> mergeInteval(List<Interval> nonOverlapInt, Interval another){
List<Interval> merge = new ArrayList<>();
for (Interval current : nonOverlapInt){
if(current.end < another.start || another.end < current.start){
merge.add(current);
}
else{
another.start = current.start < another.start ? current.start : another.start ;
another.end = current.end < another.end ? another.end : current.end;
}
}
merge.add(another);
return merge;
}
Check this out. It may help you:- http://www.boost.org/doc/libs/1_46_0/libs/icl/doc/html/index.html
The library offers these functionalities:
1) interval_set
2) separate_interval_set
3) split_interval_set
C#
public class Interval
{
public Interval(int start, int end) { this.start = start; this.end = end; }
public int start;
public int end;
}
void AddInterval(List<Interval> list, Interval interval)
{
int lo = 0;
int hi = 0;
for (lo = 0; lo < list.Count; lo++)
{
if (interval.start < list[lo].start)
{
list.Insert(lo, interval);
hi++;
break;
}
if (interval.start >= list[lo].start && interval.start <= list[lo].end)
{
break;
}
}
if (lo == list.Count)
{
list.Add(interval);
return;
}
for (hi = hi + lo; hi < list.Count; hi++)
{
if (interval.end < list[hi].start)
{
hi--;
break;
}
if (interval.end >= list[hi].start && interval.end <= list[hi].end)
{
break;
}
}
if (hi == list.Count)
{
hi = list.Count - 1;
}
list[lo].start = Math.Min(interval.start, list[lo].start);
list[lo].end = Math.Max(interval.end, list[hi].end);
if (hi - lo > 0)
{
list.RemoveRange(lo + 1, hi - lo);
}
}
This is simply done by adding the interval in question to the end of the interval set, then performing a merge on all teh elements of the interval set.
The merge operation is well-detailed here: http://www.geeksforgeeks.org/merging-intervals/
If you're not in the mood for C++ code, here is the same things in python:
def mergeIntervals(self, intervalSet):
# interval set is an array.
# each interval is a dict w/ keys: startTime, endTime.
# algorithm from: http://www.geeksforgeeks.org/merging-intervals/
import copy
intArray = copy.copy(intervalSet)
if len(intArray) <= 1:
return intArray
intArray.sort(key=lambda x: x.get('startTime'))
print "sorted array: %s" % (intArray)
myStack = [] #append and pop.
myStack.append(intArray[0])
for i in range(1, len(intArray)):
top = myStack[0]
# if current interval NOT overlapping with stack top, push it on.
if (top['endTime'] < intArray[i]['startTime']):
myStack.append(intArray[i])
# otherwise, if end of current is more, update top's endTime
elif (top['endTime'] < intArray[i]['endTime']):
top['endTime'] = intArray[i]['endTime']
myStack.pop()
myStack.append(top)
print "merged array: %s" % (myStack)
return myStack
Don't forget your nosetests to verify you actually did the work right:
class TestMyStuff(unittest.TestCase):
def test_mergeIntervals(self):
t = [ { 'startTime' : 33, 'endTime' : 35 }, { 'startTime' : 11, 'endTime' : 15 }, { 'startTime' : 72, 'endTime' : 76 }, { 'startTime' : 44, 'endTime' : 46 } ]
mgs = MyClassWithMergeIntervalsMethod()
res = mgs.mergeIntervals(t)
assert res == [ { 'startTime' : 11, 'endTime' : 15 }, { 'startTime' : 33, 'endTime' : 35 }, { 'startTime' : 44, 'endTime' : 46 }, { 'startTime' : 72, 'endTime' : 76 } ]
t = [ { 'startTime' : 33, 'endTime' : 36 }, { 'startTime' : 11, 'endTime' : 35 }, { 'startTime' : 72, 'endTime' : 76 }, { 'startTime' : 44, 'endTime' : 46 } ]
mgs = MyClassWithMergeIntervalsMethod()
res = mgs.mergeIntervals(t)
assert res == [{'endTime': 36, 'startTime': 11}, {'endTime': 46, 'startTime': 44}, {'endTime': 76, 'startTime': 72}]

Resources