[프로그래머스] 17676. 추석 트래픽

4 분 소요

문제 링크

[프로그래머스] 17676. 추석 트래픽


풀이 과정

7개의 테스트 케이스가 안맞았던 이유

DateTimeFormatterLocalDateTime 클래스를 활용해 문제를 풀었습니다. 처음에는 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.000isBefore 메소드를 호출한 결과입니다.

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"});
    }
}

카테고리:

업데이트:

댓글남기기