codingtest

24042번 - 횡단보도

2025-05-28
3분 분량
JAVA백준

문제 링크

문제 풀이 시간 : 1시간

문제 요약

  • N개의 지역(1번부터 N번까지)이 있고, 이들을 잇는 M개의 횡단보도가 있음
  • 각 횡단보도는 1분간 파란불이 들어오고, M분 주기로 반복됨
  • i번째 입력은 i, i+M, i+2M, ... 분에 해당 횡단보도에 파란불이 들어옴
  • 사람이 횡단보도를 건널 수 있는 시간은 오직 파란불이 들어온 순간부터 1분 간뿐임
  • 시작 지점은 1번 지역, 도착 지점은 N번 지역
  • 0분부터 출발하여 최소 시간으로 도착해야 함

문제 풀이

이 문제는 보자마자 다익스트라가 떠오르는 문제였다.

하지만 일반적인 다익스트라와는 다르게 간선을 특정 시간에만 사용할 수 있기 때문에 간선을 사용할 수 있는 조건을 만족하도록 대기 시간을 적절하게 계산해야 한다.

횡단보도 연결 정보

java
for (int i = 0; i < M; i++) {
    st = new StringTokenizer(br.readLine());
    int u = Integer.parseInt(st.nextToken());
    int v = Integer.parseInt(st.nextToken());

    graph.get(u).add(new Node(v, i));
    graph.get(v).add(new Node(u, i));
}

i번째 입력의 횡단보도에 대해 파란불이 들어오는 시간은 i+k*M 이다. (k는 임의의 정수)

따라서 i를 해당 횡단보도의 파란불 시작 시간으로 저장해두었다.

최소 시간 계산(다익스트라 + 대기 시간)

java
public static void find() {
    PriorityQueue<State> pq = new PriorityQueue<>(Comparator.comparingLong(s -> s.time));
    pq.offer(new State(1, 0)); // 시작 지역 1번, 시간 0
    dist[1] = 0;

    while (!pq.isEmpty()) {
        State now = pq.poll();

        if (now.time > dist[now.index]) continue;

        for (Node next : graph.get(now.index)) {
            long nextTime;

            // 현재 시간이 신호 시간보다 이르면 기다릴 필요 없음
            if (now.time <= next.signalTime) {
                nextTime = next.signalTime + 1; //이동하는 시간 1분을 더함
            } else {
                // 기다려야 하는 경우: 다음 파란불 시간을 계산
                long wait = ((now.time - next.signalTime + M - 1) / M) * M;
                nextTime = wait + next.signalTime + 1; //이동 시간 1분 더함
            }

            // 더 짧은 경로를 발견하면 갱신
            if (nextTime < dist[next.to]) {
                dist[next.to] = nextTime;
                pq.offer(new State(next.to, nextTime));
            }
        }
    }
}

신호가 들어오는 다음 시간을 (현재 시간 - 신호 시작 시간) / M 을 통해 계산한다.

전체 코드

java
import java.io.*;
import java.util.*;

class Main {
    public static int n, m;
    public static List<List<Node>> graph;
    public static long[] dist;

    static class Node {
        int to; //도착 노드 번호
        int signalTime; // 횡단 보도에 처음 파란불이 들어오는 시간

        public Node(int to, int signalTime) {
            this.to = to;
            this.signalTime = signalTime;
        }

    }
    static class State { 
        int index; //현재 번호
        long time; //현재까지 걸린 시간

        public State(int index, long time) {
            this.index = index;
            this.time = time;
        }

    }

    public static void find() {
        PriorityQueue<State> pq = new PriorityQueue<>(Comparator.comparingLong(s -> s.time));
        pq.offer(new State(1, 0));
        dist[1] = 0;

        while (!pq.isEmpty()) {
            State now = pq.poll();
	          // 이미 더 짧은 경로로 방문되었다면 패스
            if (now.time > dist[now.index]) continue;
            // 현재 노드에서 이동 가능한 모든 이웃 체크
            for (Node next : graph.get(now.index)) {
                long nextTime;
                // 현재 시간이 signalTime 이전이라면 다음 파란불 시각 이전이라 해당 시간까지 기다렸다 출발
                if (now.time <= next.signalTime) {
                    nextTime = next.signalTime + 1; //이동 시간 1 더해줌
                } else { //현재 시간이 signalTime 이후라면 파란불이 지났으므로 다음 파란불을 기다려야 함
                    long wait = ((now.time - next.signalTime + m - 1) / m) * m; // 다음 파란불까지의 시간 계산
                    nextTime = wait + next.signalTime + 1; //이동 시간 계산
                }
								//더 짧은 시간으로 업데이트
                if (dist[next.to] > nextTime) {
                    dist[next.to] = nextTime;
                    pq.offer(new State(next.to, nextTime));
                }
            }
        }
    }

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());

        n = Integer.parseInt(st.nextToken());
        m = Integer.parseInt(st.nextToken());

        graph = new ArrayList<>();
        for (int i = 0; i <= n; i++) {
            graph.add(new ArrayList<>());
        }

        for (int i = 0; i < m; i++) {
            st = new StringTokenizer(br.readLine());
            int a = Integer.parseInt(st.nextToken());
            int b = Integer.parseInt(st.nextToken());

            graph.get(a).add(new Node(b, i));
            graph.get(b).add(new Node(a, i));
        }

        dist = new long[n + 1];
        Arrays.fill(dist, Long.MAX_VALUE);

        find();
        System.out.println(dist[n]);
    }
}

함께 읽으면 좋은 글

코딩 테스트2026-04-02

5430번 - AC

함수 R은 배열을 뒤집는 연산 함수 D는 첫 번째 원소를 버리는 연산 단, 배열이 비어 있을 때 D를 수행하면 error 주어진 함수 문자열을 순서대로 수행한 뒤 최종 배열을 출력하는 문제 처음에는 문제 설명 그대로 R이 나올 때마다 배열을 실제로 뒤집고, D가 나오면 맨 앞…

코딩 테스트2026-04-02

1966번 - 프린터 큐

여러 문서가 큐에 들어 있고, 각 문서마다 중요도가 있음 맨 앞 문서를 확인했을 때, 더 높은 중요도의 문서가 뒤에 하나라도 있으면 맨 뒤로 보냄 그렇지 않으면 바로 인쇄 특정 위치의 문서가 몇 번째로 인쇄되는지 구하는 문제 이 문제는 프린터의 동작을 그대로 구현하면 되는…

코딩 테스트2026-03-29

1158 - 요세푸스 문제

1번부터 N번까지 사람이 원형으로 앉아 있음 순서대로 K번째 사람을 제거 모든 사람이 제거될 때까지 반복 제거되는 순서를 출력하는 문제 이 문제는 원형으로 순회하면서 K번째를 제거하는 과정을 그대로 구현하면 된다.

코딩 테스트2026-03-29

2164번 - 카드2

1부터 N까지 카드가 순서대로 쌓여 있음 맨 위 카드를 버리고, 그다음 맨 위 카드를 맨 아래로 옮기는 과정을 반복 마지막에 남는 카드 번호를 구하는 문제 이 문제는 규칙을 찾기보다, 문제에서 하라는 과정을 그대로 구현하면 된다. 카드 더미에서 반복되는 동작은 두 가지다.