[프로그래머스(Programmers)] [1차] 셔틀버스 (java) /2018 KAKAO BLIND RECRUITMENT

2
post-thumbnail

안녕하세요! 오늘은 프로그래머스의 셔틀버스 문제를 풀어보겠습니다. 이 문제는 2018 KAKAO BLIND RECRUITMENT 에서 출제되었습니다.


1) 문제 링크

https://programmers.co.kr/learn/courses/30/lessons/17678

2) 문제 풀이

✔ timetable sort

가장 일찍 온 사람 순서대로 차에 타야하는데 timetable배열이 정렬되어 있지 않은 채로 주어집니다. 따라서 다른 크루가 대기열에 도착하는 시각인 timetable을 오름차순으로 정렬해줍니다.

✔ 시간을 나타내는 Time class 생성

이 문제에서는 셔틀의 도착 시간, 크루가 대기열에 도착한 시간 등 "시간"이라는 개념이 계속해서 사용됩니다. JAVA에서 제공하는 Date 클래스를 사용할지, 새 class를 만들지 고민하던 끝에 Time이라는 새로운 class를 만들었습니다. 현재 자바에서는 Date 클래스 내 함수 대부분이 deprecated 되어있고, 사용을 지양하라는 의견이 많았던 것이 이유였습니다. (푸는 내내 안쓰길 잘했다는 생각이 들었습니다.!!)

✔ 콘은 무조건 마지막버스를 타야함

콘은 첫 번째 버스, 두 번째 버스에는 관심이 없습니다. 어찌됐건간에 "제일 마지막" 에만 타면 되니까요!! 따라서 콘은 무조건 마지막버스를 타야 합니다. 마지막버스니까 뒤에서부터 계산하면 편할 것 같지만, 그렇지 않습니다. 이전에 온 i-1번째 버스를 타지 못해서 어쩔 수 없이 i번째 버스를 타는 사람이 있을 수도 있기 때문입니다.
그러므로, 첫 번째 셔틀부터 차례차례 다른 크루들을 태우고, 마지막 버스에서만 콘이 타야 할 부분을 고려해주면 됩니다.

✔ 마지막 버스의 만원여부에 따라 콘의 도착 시간 결정

마지막 버스의 만원여부에 따라 콘이 대기열에 도착해야 하는 시간이 결정됩니다.

  • 만원이 아니라면 : 버스가 출발하는 시간에 도착하면 됨
  • 만원이라면 : 버스에 타는 제일 마지막 사람보다 1분 빨리 도착하면 됨

3) 전체 코드

import java.util.Arrays;

public class Solution {
    public String solution(int n, int t, int m, String[] timetable) {
        Arrays.sort(timetable);

        int crewIdx = 0;			//몇 번째 크루부터 버스에 타야하는지 나타내는 index
        int lastBusCrewNum = 0;
        boolean isLastBusFull = false;

        Time busTime;				//i번째 버스가 도착한 시간
        Time lastBusTime = new Time(9, 0);	//i-1번째 버스가 도착한 시간
        Time conTime;				//콘이 대기열에 도착해야하는 시간
        Time lastBusCrew = new Time(1000, 1000);//마지막 버스에 타는 마지막 크루의 대기열 도착한 시간
        
        for (int i = 1; i <= n; i++) {
            if (i == 1) {	//첫번째 셔틀 시간은 9시임
                busTime = lastBusTime;
            } else {		//첫번째 셔틀 이후에는 직전 셔틀 도착시간 + t분 후
                busTime = lastBusTime.makeNewBusTime(t);
            }

	    //셔틀이 도착했을 때 대기열에 있던 크루들을 셔틀에 태우는 로직
            for (int j = 1; j <= m; j++) {
                Time crewTime = stringToTime(timetable[crewIdx]);

                if ((busTime.hour > crewTime.hour) 
                	|| (busTime.hour == crewTime.hour 
                    	    && busTime.minute >= crewTime.minute)) {
                    if (i == n) {
                        lastBusCrew = crewTime;
                        lastBusCrewNum += 1;
                    }

                    crewIdx += 1;

                    if (i == n && j == m) {
                        isLastBusFull = true;
                    }

                    if (crewIdx >= timetable.length) break;
                } else {
                    break;
                }
            }
            lastBusTime = busTime;
        }

	//마지막 버스의 만원여부에 따라 conTime 결정
        if (isLastBusFull) {
            if (lastBusCrewNum == m) {
                conTime = lastBusCrew.makeNewBusTime(-1);
            } else {
                conTime = lastBusCrew;
            }
        } else {
            conTime = lastBusTime;
        }
        return timeToString(conTime);
    }
	
    //String에서 Time객체로 변환
    private Time stringToTime(String str) {
        String[] array = str.split(":");
        int hour = Integer.parseInt(array[0]);
        int minute = Integer.parseInt(array[1]);

        return new Time(hour, minute);
    }

    //Time객체에서 String으로 변환
    private String timeToString(Time time) {
        StringBuilder sb = new StringBuilder();

        if (time.hour < 10) {
            sb.append("0");
        }
        sb.append(time.hour + ":");

        if (time.minute < 10) {
            sb.append("0");
        }
        sb.append(time.minute);

        return sb.toString();
    }
}

class Time {
    int hour;
    int minute;

    Time(int hour, int minute) {
        this.hour = hour;
        this.minute = minute;
    }
	
    //t분 후 Time을 새로운 Time객체로 return
    public Time makeNewBusTime(int t) {
        int newHour = this.hour;
        int newMin = this.minute + t;

        if (t >= 0) {
            if (newMin / 60 > 0) {
                newHour += newMin / 60;
                newMin = newMin % 60;
            }
        } else {	//t가 마이너스일 경우는 -1일때밖에 없기 때문에 임의로 시간 설정
            if (newMin < 0) {
                newHour -= 1;
                newMin = 59;
            }
        }

        return new Time(newHour, newMin);
    }

    @Override
    public String toString() {
        return "Time{" +
                "hour=" + hour +
                ", minute=" + minute +
                '}';
    }
}

4-1) 느낀점 - 아쉬웠던 부분

✔ 추가 테스트케이스 찾지 못함

처음에는 테스트케이스 세 개를 틀렸었습니다. 그리고는 이전처럼 또 틀린 이유를 찾지 못한 채 해맸습니다.. 확인 결과 마지막 버스의 만원여부에 따라 콘의 도착 시간 결정되는 사실은 알았지만, 마지막 버스가 만원이면 제일 마지막 사람이 아니라 제일 처음 사람보다 1분 빨리 도착하는 로직을 짰던 것이 이유였습니다. 전체 로직에 대한 답을 본 건 아니지만, 틀린 이유를 혼자 힘으로 찾아서 풀었다면 더 좋았을 듯 합니다.

4-2) 느낀점 - 잘한 부분

✔ Time 객체 생성

Time객체를 생성해서, 시간 관련 로직을 Time객체 내에서 해결한 점은 잘했다고 생각합니다. 객체를 생성해서 따로 관리했기 때문에 틀린 케이스를 찾은 후 유지보수가 매우 쉬웠습니다.


[참고한 곳]
https://programmers.co.kr/questions/20374

0개의 댓글