[프로그래머스] 17676. 추석 트래픽
문제 링크
풀이 과정
7개의 테스트 케이스가 안맞았던 이유
DateTimeFormatter 와 LocalDateTime 클래스를 활용해 문제를 풀었습니다. 처음에는 LocalDateTime 대신에 LocalTime 클래스를 이용했는데, 문제에서 코드 실행 탭을 통해 보여지는 테스트 입력에 다음과 같은 입력이 있었습니다.
2016-09-15 00:00:00.000 3s
위의 입력 예의 처리 시작 날짜는 하루 전(2016-09-14 23:59:57.001) 으로 바뀌게 됩니다. 이는 시간만 가공하는 LocalTime 클래스로는 시간 비교를 할 때 예상과는 다른 결과를 내놨습니다. 아래 코드는 위 예제의 처리 시작 시간 23:59:57.001 과 응답 완료 시간 00:00:00.000 에 isBefore 메소드를 호출한 결과입니다.
LocalTime prev = LocalTime.parse("23:59:57.001", dtf);
LocalTime after = LocalTime.parse("00:00:00.000", dtf);
// 23:59:57.001 이 00:00:00.000 보다 이전이냐?
System.out.println("prev.isBefore(after) : " + prev.isBefore(after)); // false
위와 같이 날짜가 넘어가는 예외 상황들 때문에 LocalTime 이 아닌 LocalDateTime 클래스를 사용해야지만 모든 테스트 케이스를 맞을 수 있었습니다.

문제 조건인 처리 시간은 시작 시간과 끝 시간을 포함한다는 점을 유의해야 합니다. 위 그림과 같이 첫 번째 로그가 끝나는 시간부터 1초 구간은 첫 번째 로그와 두 번째 로그를 둘 다 처리할 수 있습니다.
for (int i = 0; i < lines.length; i++) {
    int len = lines[i].length();
    lines[i] = lines[i].substring(0, len - 1);
    String[] tmp = lines[i].split(" ");
    S[i] = tmp[0] + " " + tmp[1];
    T[i] = tmp[2];
}
제일 먼저 문제에서 주어진 lines 문자열 배열을 통해 각 로그의 응답 완료 시간(S)와 처리 시간(T)를 분리합니다. 각 로그 맨 뒤에 붙은 문자 s를 제거하기 위해 substring 메소드를 이용했습니다. 또한 응답 완료 시간 배열 S에는 시간 뿐만 아니라 날짜도 포함시켰습니다. 이는 LocalTime 클래스가 아닌 LocalDateTime 클래스를 활용하기 위함입니다.
for (int i = 0; i < lines.length; i++) {
    LocalDateTime start = LocalDateTime.parse(S[i], dtf).minusNanos((long) (Double.parseDouble(T[i]) * NANO)).plusNanos((long) (0.001 * NANO));
    list.add(new Time(start, LocalDateTime.parse(S[i], dtf)));
}
Collections.sort(list);
다음으로 각 로그를 파싱해 구한 배열 S(응답 완료 시간)와 T(처리 시간)를 통해 각 로그의 처리 시작 시간 start 를 구합니다. LocalTime 객체에서 밀리초를 빼기 위해 minusNanos 메소드를 사용했습니다. LocalTime 클래스에는 밀리초에 관한 연산 메소드를 제공하지 않으므로, 나노 단위로의 변경이 필요했습니다. 처리 시간을 뺀 뒤에는, 시작 시간 또한 포함시키기 위해 0.001 초를 더해( plusNanos )줬습니다. 연산이 끝나면 리스트에 넣고 처리 시작 시간을 기준으로 오름차순으로 정렬합니다.
for (int i = 0; i < list.size(); i++) {
    LocalDateTime start = list.get(i).start;
    LocalDateTime end = list.get(i).end;
    LocalDateTime startPlusOne = start.plusSeconds(1).minusNanos((long) (0.001 * NANO));
    LocalDateTime endPlusOne = end.plusSeconds(1).minusNanos((long) (0.001 * NANO));
    int cntStart = 0, cntEnd = 0;
    for (int j = 0; j < list.size(); j++) {
        LocalDateTime targetStart = list.get(j).start;
        LocalDateTime targetEnd = list.get(j).end;
        if ((targetStart.isBefore(startPlusOne) || targetStart.equals(startPlusOne)) && (targetEnd.isAfter(start) || targetEnd.equals(start)))
            cntStart++;
        if ((targetStart.isBefore(endPlusOne) || targetStart.equals(endPlusOne)) && (targetEnd.isAfter(end) || targetEnd.equals(end)))
            cntEnd++;
        answer = Math.max(answer, cntStart);
        answer = Math.max(answer, cntEnd);
    }
}
정렬이 끝났다면 각 로그를 기준으로 모든 가능한 경우의 수를 구합니다. 각 로그의 처리 시작 시간( start ) 과 응답 완료 시간( end ) 각각에 대해 1초를 더해, 해당 구간 안에 존재하는 모든 요청들을 셉니다.
if ((targetStart.isBefore(startPlusOne) || targetStart.equals(startPlusOne)) && (targetEnd.isAfter(start) || targetEnd.equals(start)))
    cntStart++;
if ((targetStart.isBefore(endPlusOne) || targetStart.equals(endPlusOne)) && (targetEnd.isAfter(end) || targetEnd.equals(end)))
    cntEnd++;
구간 내에 속해있는 지 판별은 start ~ startPlusOne, end ~ endPlusOne 의 두 구간을 기준으로 합니다. 아래와 같은 target 로그 막대들이 위에 적힌 두 구간 내에 들어오는지 양 끝점을 기준으로 판단합니다.

코드
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collections;
public class Main {
    public static int solution(String[] lines) {
        final int NANO = 1000000000;
        int answer = 0;
        String[] S = new String[lines.length];
        String[] T = new String[lines.length];
        for (int i = 0; i < lines.length; i++) {
            int len = lines[i].length();
            lines[i] = lines[i].substring(0, len - 1);
            String[] tmp = lines[i].split(" ");
            S[i] = tmp[0] + " " + tmp[1];
            T[i] = tmp[2];
        }
        ArrayList<Time> list = new ArrayList<>();
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
        for (int i = 0; i < lines.length; i++) {
            LocalDateTime start = LocalDateTime.parse(S[i], dtf).minusNanos((long) (Double.parseDouble(T[i]) * NANO)).plusNanos((long) (0.001 * NANO));
            list.add(new Time(start, LocalDateTime.parse(S[i], dtf)));
        }
        Collections.sort(list);
        for (int i = 0; i < list.size(); i++) {
            LocalDateTime start = list.get(i).start;
            LocalDateTime end = list.get(i).end;
            LocalDateTime startPlusOne = start.plusSeconds(1).minusNanos((long) (0.001 * NANO));
            LocalDateTime endPlusOne = end.plusSeconds(1).minusNanos((long) (0.001 * NANO));
            int cntStart = 0, cntEnd = 0;
            for (int j = 0; j < list.size(); j++) {
                LocalDateTime targetStart = list.get(j).start;
                LocalDateTime targetEnd = list.get(j).end;
                if ((targetStart.isBefore(startPlusOne) || targetStart.equals(startPlusOne)) && (targetEnd.isAfter(start) || targetEnd.equals(start)))
                    cntStart++;
                if ((targetStart.isBefore(endPlusOne) || targetStart.equals(endPlusOne)) && (targetEnd.isAfter(end) || targetEnd.equals(end)))
                    cntEnd++;
                answer = Math.max(answer, cntStart);
                answer = Math.max(answer, cntEnd);
            }
        }
        return answer;
    }
    public static class Time implements Comparable<Time> {
        LocalDateTime start;
        LocalDateTime end;
        public Time(LocalDateTime start, LocalDateTime end) {
            this.start = start;
            this.end = end;
        }
        @Override
        public int compareTo(Time o) {
            if (this.start.getHour() == o.start.getHour()) {
                if (this.start.getMinute() == o.start.getMinute()) {
                    if (this.start.getSecond() == o.start.getSecond()) {
                        return this.start.getNano() - o.start.getNano();
                    }
                    return this.start.getSecond() - o.start.getSecond();
                }
                return this.start.getMinute() - o.start.getMinute();
            }
            return this.start.getHour() - o.start.getHour();
        }
    }
    public static void main(String[] args) {
        solution(new String[]{"2016-09-15 00:00:00.000 3s"});
//        solution(new String[]{"2016-09-15 01:00:04.001 2.0s", "2016-09-15 01:00:07.000 2s"});
//        solution(new String[]{"2016-09-15 01:00:04.002 2.0s", "2016-09-15 01:00:07.000 2s"});
//        solution(new String[]{"2016-09-15 20:59:57.421 0.351s", "2016-09-15 20:59:58.233 1.181s", "2016-09-15 20:59:58.299 0.8s", "2016-09-15 20:59:58.688 1.041s", "2016-09-15 20:59:59.591 1.412s",
//                "2016-09-15 21:00:00.464 1.466s", "2016-09-15 21:00:00.741 1.581s", "2016-09-15 21:00:00.748 2.31s", "2016-09-15 21:00:00.966 0.381s", "2016-09-15 21:00:02.066 2.62s"});
    }
}
 
      
    
댓글남기기