0

Algorithm asked in one of the top company's interview, but I am not able to find a feasible solution. Need expert advice.

Suppose a student wants to attend maximum number of classes in a collage in a day (without any class overlap).

Input Format

  • The first line contains an integer n which gives the number of subjects offered on that day.
  • The next n lines follow containing the subject name (which is a string) followed by the start and end time for that subject in 24-hour format: hh:mm

For eg: Maths 10:00 11:00
Note: The timings are given in a 24-hour format and the subject names do not have spaces between them.

Output Format
The output should contain a number representing the maximum number of classes the student can choose.

Constraints
2 <= n <= 100
start time of a class < end time of class

Sample Input

4
Maths 16:00 18:00
ComputerScience 12:00 13:00
Physics 12:30 14:00
Chemistry 14:00 16:30

Sample Output

2

Explanation
ComputerScience starts the earliest and ends the earliest, so we take it first. After that, we cannot take Physics because it starts before ComputerScience is over. So we will take the next class, that is, Chemistry. But after Chemistry we cannot take Maths as Maths class starts before Chemistry class ends. So we can schedule a maximum of 2 classes for the day.

Below is my solution but I am not getting correct answer:

private void getMaxClass(String input) {

    Map<String, Long> classTime = new LinkedHashMap<>();
    Map<String, List<String>> timeMap = new LinkedHashMap<>();
    String[] split = input.split(" ");
    String subject = split[0];
    String StartTime = split[1];
    String endTime = split[2];
    List<String> lvalue = new ArrayList<>();
    lvalue.add(StartTime);
    lvalue.add(endTime);
    timeMap.put(subject, lvalue);
    long difference = FineDifferenceInTime(StartTime, endTime);
    classTime.put(subject, difference);
    int count = 0;
    Date date1 = null;
    Date date2 = null;
    Map<String, Long> sortedByValueDesc = classTime.entrySet().stream()
            .sorted(Map.Entry.<String, Long>comparingByValue())
            .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
    for (Map.Entry<String, Long> entry : sortedByValueDesc.entrySet()) {
        String sub = entry.getKey();
        List<String> startEnd = timeMap.get(sub);
        Date dateBefore = null;
        Date dateAfter = null;
        SimpleDateFormat format = new SimpleDateFormat("HH:mm");
        try {
            dateBefore = format.parse(startEnd.get(0));
            dateAfter = format.parse(startEnd.get(1));
        } catch (ParseException e) {
            e.printStackTrace();
        }
        if (count == 0) {
            count++;
            try {
                date1 = format.parse(startEnd.get(0));
                date2 = format.parse(startEnd.get(1));
            } catch (ParseException e) {
                e.printStackTrace();
            }
        }
        if (dateBefore.after(date1) && dateBefore.before(date2)) {
            timeMap.remove(sub);
        }
    }
    System.out.println(timeMap.size());
}
Codextor
  • 120
  • 2
  • 10
Hitesh Kumar
  • 45
  • 1
  • 14
  • 2
    What is your exact input and output and how does it differ from what you have expected? "Not getting the correct answer" could mean anything from no output at all to an exception. – BluesSolo Feb 12 '19 at 13:07
  • If your input is the whole string for all 4 classes, then why are you only taking first 3 items like split[0], split[1] and split[2]? Do you realize that there are 12 items in that `split` array? – Prasad Karunagoda Feb 12 '19 at 13:18
  • suppose input is coming line by line and I am splitting it and saving it to Map. My logic is to get the minimum time difference between start and end time and compare date.after and date.before and remove it from Map but it is giving wrong answer. – Hitesh Kumar Feb 13 '19 at 08:01
  • 1
    thanks @GhostCat I will update it correctly – Hitesh Kumar Feb 13 '19 at 08:02
  • 2
    Dont put more information into comments. Enhance your question, and show exactly what goes in, what comes out, and what you would have expected instead. – GhostCat Feb 13 '19 at 08:04
  • A similar one: https://stackoverflow.com/questions/42752079/max-coverage-disjoint-intervals – Andrew Scott Feb 18 '19 at 10:07
  • What if you have a class that starts at 9am and ends at 6pm? Your code will output 1, no? :) i.e. your code outputs the correct answer for this specific case. You failed to see the larger constraints. – João Neto Feb 18 '19 at 10:08
  • A few notes: **1.** You should follow the Java Naming Conventions: variable names and method names always start with lowercase, i.e. `StartTime` should be `startTime` and `FineDifferenceInTime` should be `fineDifferenceInTime`. **2.** You shouldn't use the obsolete `Date`, `Calendar` and `SimpleDateFormat` classes, use the new Java Date and Time API (within the `java.time` package). **3.** You should format your post to make it better readable. – MC Emperor Feb 18 '19 at 10:39

5 Answers5

1

This is known in literature as the Interval Scheduling Problem. There are many ways to solve it, but since it is NP-complete (as there is a a polynomial reduction from VC) you'll necessarily need to explore all the combinations.

Greedy algorithms do exist (as is yours and the solution by @PriyankaDeshmukh), but they don't guarantee you the exact solution for all instances of the problem.

The solution below is a simple tree search: at each level we decide if we take a given course or not and move on to decide on the next course.

You could also implement a dynamic programming solution.

Here is a very good blog post covering solutions to the Interval Scheduling Problem.

decision tree


I modelled a student class the following way:

class StudentClass {
    public int _start;
    public int _end;
    public String _name;
    public StudentClass(String name, int start, int end) {
        _name = name;
        _start = start;
        _end = end;
    }
    public boolean overlapsWith(StudentClass other) {
        return _start < other._end && _end > other._start;
    }
    public String toString() {
        return "[" + _start + " - " + _end + "] " + _name;
    }
}

There are classes to represent the time of the day, but their syntax/instantiation is a bit annoying/verbose -- feel free to improve this answer though! My Java is also very rusty, so feel free to correct me :-)


The Schedule class has a getMaxSchedule() which returns the solution to the problem -- what is the maximum number of classes a student can take, such that none of them overlap?

There are a few ways to optimize it, but I left it as-is as I believe it's easier to be understood.

public class Schedule {

    List<StudentClass> _classes = new LinkedList<>();

    public void addClass(String name, int startTime, int endTime) {
        _classes.add(new StudentClass(name, startTime, endTime));
    }

    private int getMaxSchedule(int index, Collection<StudentClass> selected) {
        // check if we reached the end of the array
        if (index >= _classes.size()) {
            return 0;
        }

        StudentClass current = _classes.get(index);

        // check if taking this class doesn't conflict with the
        // previously-selected set of classes
        boolean canTakeThisClass = true;
        for (StudentClass other : selected) {
            if (current.overlapsWith(other)) {
                canTakeThisClass = false;
                break;
            }
        }

        // check best schedule if we don't take this class
        int best = getMaxSchedule(index + 1, selected);

        // check best schedule if we take this class (if such is possible)
        if (canTakeThisClass) {
            selected.add(current);
            best = Math.max(best, 1 + getMaxSchedule(index + 1, selected));
            selected.remove(current);
        }

        return best;
    }

    public int getMaxSchedule() {
        Collection<StudentClass> selected = new ArrayList<>();
        return getMaxSchedule(0, selected);
    }
}

You can see the result is 3 for your concrete problem through the following:

public static void main(String[] args) {
    Schedule s = new Schedule();
    s.addClass("Maths", 1600, 1800);
    s.addClass("Computer Science", 1200, 1300);
    s.addClass("Physics", 1230, 1400);
    s.addClass("Chemistry", 1400, 1630);
    System.out.println("maximum classes: " + s.getMaxSchedule());
}
João Neto
  • 1,732
  • 17
  • 28
0

I tried it with Python. It gives correct output. I got it sorted by start time of class.

sorted_by_start = [{'sub': 'ComputerScience', 'start': '12:00', 'end': '13:00', 
'duration': 60}, {'sub': 'Physics', 'start': '12:30', 'end': '14:00', 'duration': 
90}, {'sub': 'Chemistry', 'start': '14:00', 'end': '16:30', 'duration': 150}, 
{'sub': 'Maths', 'start': '16:00', 'end': '18:00', 'duration': 120}]

possible_sub = set()

for a, b in itertools.combinations(sorted_by_start, 2):
    strt_tme = datetime.datetime.strptime(a["end"], '%H:%M')
    end_tme = datetime.datetime.strptime(b["start"], '%H:%M')
      if(strt_tme <= end_tme) :
        possible_sub.add((a["sub"],b["sub"]))

print("A student can attend these combinations of subject classes:",possible_sub)
print("Maximum classes student can attend in a day is: ",max(map(len,possible_sub)))

Here trick part is deciding how many combinations you can make. So, that you can do by adding additional for loop ranging from 2 to length of sorted_list and pass i to combinations(sorted_list, i) like this.

Ouput is :

A student can attend these combinations of subject classes:  {('Physics', 'Maths'), ('Physics', 'Chemistry'), ('ComputerScience', 'Chemistry'), ('Compu
terScience', 'Maths')}
Maximum classes student can attend in a day is:  2
pride
  • 85
  • 1
  • 11
  • What if you have a class that starts at 9am and ends at 6pm? Your code will output `1`, no? :) – João Neto Feb 18 '19 at 10:08
  • So you have to sort it by the order of the finish times and then discard anything that conflicts. – Andrew Scott Feb 18 '19 at 10:17
  • @JoãoNeto For that, you can add if len(sorted_by_start) == 1: possible_sub.add(add_only_sub) else the rest part. – pride Feb 18 '19 at 10:32
  • @PriyankaDeshmukh I didn't fully understand that, but I don't think that will work for the general case. This is a known NP-complete problem and the only way you can get the optimal answer is through exhaustive search. Without that, your algorithm will only be able to give an approximation. – João Neto Feb 18 '19 at 10:43
  • @JoãoNeto Could you post your answer for this? It will help me learn. – pride Feb 18 '19 at 11:12
  • @pride done :) sorry for not replying and taking too long – João Neto Feb 19 '19 at 18:15
0
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

class InputSubject implements Comparable<InputSubject>{
    String subject;
    String startTime;
    String endTime;
    InputSubject(){
        
    }
    InputSubject(String subject, String startTime, String endTime){
        this.subject = subject;
        this.startTime = startTime;
        this.endTime =  endTime;
    }
    
    public String getSubject() {
        return subject;
    }
    public void setSubject(String subject) {
        this.subject = subject;
    }
    public String getStartTime() {
        return startTime;
    }
    public void setStartTime(String startTime) {
        this.startTime = startTime;
    }
    public String getEndTime() {
        return endTime;
    }
    public void setEndTime(String endTime) {
        this.endTime = endTime;
    }
    @Override
    public int compareTo(InputSubject o) {
        
        return this.endTime.compareTo(o.endTime);
    }

}

public class SubjectSort {
    
    static int getToatlSubject(List<InputSubject> list){
        String sTime = null;
        String eTime =  null;
        int count = 0;
        int noOfSubject = 0;
        for(InputSubject object : list){
            if(count == 0){
                count++;
                eTime = object.getEndTime();
                noOfSubject ++;
            }
            else {
                if(object.getStartTime().compareTo(eTime) > 0){
                    eTime = object.getEndTime();
                    noOfSubject ++;
                }
            }
        }
        return noOfSubject;
    }

    public static void main(String[] args) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        try {
            int days = Integer.parseInt(reader.readLine());
            for(int i = 0 ; i < days ;i++){
                int sub = Integer.parseInt(reader.readLine());
                List<InputSubject> list = new ArrayList<>();
                for(int k = 0 ; k < sub ; k++){
                    InputSubject inputSubject = null;
                    String subDetails =  reader.readLine();
                    String[] subAndTimes = subDetails.split(" ");
                    inputSubject = new InputSubject(subAndTimes[0],subAndTimes[1],subAndTimes[2]);
                    list.add(inputSubject);
                }
                Collections.sort(list);
                System.out.println(getToatlSubject(list));
            }
        
        } catch (Exception e) {
        }

    }

}
  • 3
    Thank you for contributing an answer. Would you kindly edit your answer to to include an explanation of your code? That will help future readers better understand what is going on, and especially those members of the community who are new to the language and struggling to understand the concepts. – Jeremy Caney Dec 14 '20 at 01:40
0
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

class InputSubject implements Comparable<InputSubject>{
    String subject;
    String startTime;
    String endTime;
    InputSubject(){
        
    }
    InputSubject(String subject, String startTime, String endTime){
        this.subject = subject;
        this.startTime = startTime;
        this.endTime =  endTime;
    }
    
    public String getSubject() {
        return subject;
    }
    public void setSubject(String subject) {
        this.subject = subject;
    }
    public String getStartTime() {
        return startTime;
    }
    public void setStartTime(String startTime) {
        this.startTime = startTime;
    }
    public String getEndTime() {
        return endTime;
    }
    public void setEndTime(String endTime) {
        this.endTime = endTime;
    }
    @Override
    public int compareTo(InputSubject o) {
        
        return this.endTime.compareTo(o.endTime);
    }

}

public class solution {
    
    static int getToatlSubject(List<InputSubject> list){
        String sTime = null;
        String eTime =  null;
        int count = 0;
        int noOfSubject = 0;
        for(InputSubject object : list){
            if(count == 0){
                count++;
                eTime = object.getEndTime();
                noOfSubject ++;
            }
            else {
                if(object.getStartTime().compareTo(eTime) >= 0){
                    eTime = object.getEndTime();
                    noOfSubject ++;
                }
            }
        }
        return noOfSubject;
    }

    public static void main(String[] args) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        try {
            int days = Integer.parseInt(reader.readLine());
            for(int i = 0 ; i < days ;i++){
                int sub = Integer.parseInt(reader.readLine());
                List<InputSubject> list = new ArrayList<>();
                for(int k = 0 ; k < sub ; k++){
                    InputSubject inputSubject = null;
                    String subDetails =  reader.readLine();
                    String[] subAndTimes = subDetails.split(" ");
                    inputSubject = new InputSubject(subAndTimes[0],subAndTimes[1],subAndTimes[2]);
                    list.add(inputSubject);
                }
                Collections.sort(list);
                System.out.println(getToatlSubject(list));
            }
        
        } catch (Exception e) {
        }

    }

}
  • Thank you for your answer. Can you improve it adding some explanation about your code? It will help in future when someone try to use your solution :) – gianluca aguzzi May 08 '21 at 19:52
-1

i had same question in a coding round of a recruitment and I have solved it the following way and it gives correct answer. But unfortunately this code passed only 1 test case given in the challenge. I believe that the test cases were incorrect. Can anyone point out if I have missed something in code which might have led to this code failing other test cases?

import java.util.*;
import java.time.*;
import java.time.format.DateTimeFormatter; 
import static java.time.temporal.ChronoUnit.*;

class solution {

    public static void main(String[] args) {

        Scanner sc = new Scanner(System.in);
        int t = sc.nextInt();
        for (int i = 0; i < t; i++) {
            int n=sc.nextInt();
            String trash=sc.nextLine();
            HashMap subjects=new HashMap<String,String>();
            HashMap starttime=new HashMap<String,LocalTime>();
            HashMap endtime=new HashMap<String,LocalTime>();
            HashMap length=new HashMap<String,Long>();
            for (int j = 0; j < n; j++){
                String classes = sc.nextLine();
                String[] classesArray=classes.split(" ");
                String subject=classesArray[0];
                if(classesArray[1].split(":")[0].length()==1){
                    classesArray[1]="0"+classesArray[1];
                }
                if(classesArray[2].split(":")[0].length()==1){
                    classesArray[2]="0"+classesArray[2];
                }
                LocalTime startTime=LocalTime.parse(classesArray[1]);
                LocalTime endTime=LocalTime.parse(classesArray[2]);
                DateTimeFormatter formatter = DateTimeFormatter.ISO_TIME; 
                Long lengthLecture=MINUTES.between(startTime, endTime);
                subjects.put(subject,subject);
                starttime.put(subject,startTime);
                endtime.put(subject,endTime);
                length.put(subject,lengthLecture);
                String value = startTime.format(formatter);
                String value1 = endTime.format(formatter);
                // System.out.printf("Sub:%s st:%s et:%s length:%d\n",subject,value,value1,lengthLecture);
            }
            findMax(subjects,starttime,endtime,length);
            //System.out.println(num);
        }
    }
    public static void findMax(HashMap<String,String> subs,HashMap<String,LocalTime> strt,HashMap<String,LocalTime> endt,HashMap<String,Long> length){
      int number=0;
      List<Integer> list = new ArrayList<Integer>();
      String curr,next1;
      for (Map.Entry<String,String> entry : subs.entrySet()){
      //System.out.println("Checkign for number: "+entry.getKey());
      number=findnext(entry.getKey(),strt,endt);
      // System.out.println("Number is: "+number);
      list.add(number);
      }
      System.out.println(Collections.max(list));
    }
   
    public static int findnext(String subjt,HashMap<String,LocalTime> strt,HashMap<String,LocalTime> endt){
        String sub=subjt;
        int number=1;
        LocalTime substtime=strt.get(subjt);
        LocalTime subedtime=endt.get(subjt);
        Long timetillstart=922337203L;
        for (Map.Entry<String,LocalTime> entry : strt.entrySet()){
            if((entry.getValue().compareTo(subedtime)>=0) && (MINUTES.between(subedtime, entry.getValue())<=timetillstart)){
                sub=entry.getKey();
                substtime=strt.get(entry.getKey());
                timetillstart=MINUTES.between(subedtime, entry.getValue());
            }
        }
        if(sub!=subjt){
            number=number+findnext(sub,strt,endt);
        }
        return number;
    }
}
Mighty God Loki
  • 135
  • 3
  • 10