박물관에 전시실 n개가 있고 1번부터 n번까지 번호가 붙어 있다.
전시실 사이에는 복도 m개가 있고, 복도 하나 "a b"는 a번 전시실과 b번 전시실을 잇는다.
복도는 양쪽으로 다닐 수 있고, 복도 하나를 지나는 데 1분이 걸린다.
같은 두 전시실을 잇는 복도가 여러 개 있을 수 있다.
문 닫을 준비로 전시실 입구가 하나씩 잠긴다.
i번 전시실에는 tᵢ분까지 들어갈 수 있다. 딱 tᵢ분에 닿으면 들어갈 수 있고, 그보다 늦게 닿으면 들어갈 수 없다.
입구가 잠긴 뒤에도 안에 있던 사람은 언제든 나갈 수 있다.
관람객 한 명이 0분에 1번 전시실 안에 있다.
1번 전시실에는 처음부터 있으므로 t₁과 상관없이 0분에 들어간 것으로 친다.
관람객은 복도로만 움직이고, 어떤 전시실을 지나가려면 그 전시실에 들어가야 한다.
전시실마다, 관람객이 그 전시실에 들어갈 수 있는 가장 이른 시각을 구하라.
어떤 길로 가도 들어갈 수 없는 전시실의 값은 -1이다.
첫 줄에 들어갈 수 있는 전시실의 수를 출력하라. 1번 전시실도 센다.
둘째 줄에 1번부터 n번 전시실까지의 값을 차례로 빈칸 하나로 띄워 출력하라.
입력
첫 줄에 n과 m, 둘째 줄에 t₁ … tₙ, 다음 m줄에 줄마다 a와 b가 주어진다. 한 줄의 수들은 빈칸 하나로 나뉜다. 1 ≤ n ≤ 10,000, 0 ≤ m ≤ 20,000, 0 ≤ tᵢ ≤ 10,000, 1 ≤ a, b ≤ n, a ≠ b. 모두 정수다
출력
첫 줄에 들어갈 수 있는 전시실의 수, 둘째 줄에 전시실마다 가장 이른 시각(분) n개. 들어갈 수 없으면 -1
예시 1 · stdin
6 6
5 1 1 3 2 9
1 2
2 3
1 4
4 5
5 3
3 6
stdout
4
0 1 -1 1 2 -1
예시 2 · stdin
4 3
0 1 2 3
1 2
2 3
3 4
stdout
4
0 1 2 3
main.py
오류 목록00F8 ↓ · ⇧F8 ↑
입력(stdin)
실행하면 이 줄들이 차례로 input()·표준 입력으로 들어가요(실행 중에 묻지 않아요).
$ 준비됐어요
코드를 입력해 풀려면 PC에서 열어 주세요. 모바일에서는 문제와 풀이를 볼 수 있어요.
다른 언어로는?언어별 풀이 보기 · 8개 언어 풀이
너비 우선 탐색(BFS)은 큐에서 꺼내는 차례가 곧 가까운 차례라서, 어떤 전시실에 처음 닿는 때가 가장 이른 때예요. 그때 이미 잠겨 있으면 나중에 와도 잠겨 있으니, 닿는 순간에 t ≤ tᵥ를 한 번만 검사하면 돼요. 첫 예시: 3번에는 2번을 거쳐 2분에 닿지만 t₃ = 1이라 못 들어가고(5번을 거치면 3분), 그래서 t₆ = 9로 넉넉한 6번도 갈 길이 없어요. 함정 하나: 문을 무시하고 거리부터 다 구한 뒤 거리 ≤ t인 전시실만 남기면 틀려요 — 잠긴 전시실을 지나서 간 거리가 섞여서 첫 예시의 6번이 3으로 나와요. 함정 둘: t < tᵥ로 쓰면 딱 tᵥ분에 닿는 전시실을 놓쳐요(둘째 예시). 함정 셋: 떠나는 전시실의 t를 보면 틀려요 — 잠긴 뒤에도 나갈 수는 있어요. 함정 넷: 큐 대신 스택(나중에 넣은 것부터)으로 꺼내면 깊이 우선 탐색이 되어 돌아가는 길로 먼저 닿고, 그 늦은 시각 때문에 문이 잠겨요. 함정 다섯: 복도는 양쪽으로 다니니 인접 목록의 양쪽에 넣어요. 함정 여섯: 첫 줄의 수에는 1번도 들어가요. 큐는 언어마다 달라요: Python은 collections.deque의 popleft()(리스트의 pop(0)은 뒤의 것을 모두 당겨서 느려요), Java는 ArrayDeque, C#은 Queue<int>, C++은 std::queue를 써요. JavaScript·TypeScript 배열의 shift()도 뒤의 것을 당기니 배열에 push만 하고 머리 번호를 옮기며 읽어요. C는 전시실마다 큐에 한 번만 들어가므로 n칸 배열과 머리·꼬리 번호면 돼요. 크레이콘도 목록에 넣기로 넣고 머리 번호를 옮기며, 이웃은 각각 (v 에서 halls[u])로 돌아요.
Pythonmain.py · 29 줄
import sysfrom collections import dequedata = sys.stdin.read().split()n, m = int(data[0]), int(data[1])close = [0] + [int(x) for x in data[2:2 + n]] # close[v] = v번 전시실에 들어갈 수 있는 마지막 시각# 인접 목록: halls[v] = v번 전시실과 복도로 이어진 전시실들(양쪽으로 다닌다)halls = [[] for _ in range(n + 1)]for i in range(m): a, b = int(data[2 + n + 2 * i]), int(data[3 + n + 2 * i]) halls[a].append(b) halls[b].append(a)# 너비 우선 탐색(BFS): 큐에서 꺼내는 차례가 곧 도착 시각이 이른 차례다arrive = [-1] * (n + 1)arrive[1] = 0 # 1번에는 처음부터 있으니 close[1]은 보지 않는다queue = deque([1])while queue: u = queue.popleft() t = arrive[u] + 1 # 이웃 전시실에 닿는 시각 for v in halls[u]: # 처음 닿는 때가 가장 이른 때다. 그때 이미 잠겼으면 나중에 와도 잠겨 있다 if arrive[v] == -1 and t <= close[v]: arrive[v] = t queue.append(v)print(sum(1 for v in range(1, n + 1) if arrive[v] != -1))print(" ".join(map(str, arrive[1:])))
JavaScriptmain.js · 35 줄
const data = require("fs").readFileSync(0, "utf8").trim().split(/\s+/).map(Number);const n = data[0];const m = data[1];// close[v] = v번 전시실에 들어갈 수 있는 마지막 시각const close = [0];for (let v = 1; v <= n; v++) close.push(data[1 + v]);// 인접 목록: halls[v] = v번 전시실과 복도로 이어진 전시실들(양쪽으로 다닌다)const halls = Array.from({ length: n + 1 }, () => []);for (let i = 0; i < m; i++) { const a = data[2 + n + 2 * i]; const b = data[3 + n + 2 * i]; halls[a].push(b); halls[b].push(a);}// 너비 우선 탐색(BFS): 큐에서 꺼내는 차례가 곧 도착 시각이 이른 차례다const arrive = new Array(n + 1).fill(-1);arrive[1] = 0; // 1번에는 처음부터 있으니 close[1]은 보지 않는다const queue = [1];// shift()는 뒤의 것을 모두 당겨서 느리다. 머리 번호 head를 옮긴다for (let head = 0; head < queue.length; head++) { const u = queue[head]; const t = arrive[u] + 1; // 이웃 전시실에 닿는 시각 for (const v of halls[u]) { // 처음 닿는 때가 가장 이른 때다. 그때 이미 잠겼으면 나중에 와도 잠겨 있다 if (arrive[v] === -1 && t <= close[v]) { arrive[v] = t; queue.push(v); } }}console.log(queue.length); // 큐에 한 번씩 들어간 전시실이 곧 들어갈 수 있는 전시실이다console.log(arrive.slice(1).join(" "));
TypeScriptmain.ts · 37 줄
import { readFileSync } from "node:fs";const data: number[] = readFileSync(0, "utf8").trim().split(/\s+/).map(Number);const n = data[0];const m = data[1];// close[v] = v번 전시실에 들어갈 수 있는 마지막 시각const close: number[] = [0];for (let v = 1; v <= n; v++) close.push(data[1 + v]);// 인접 목록: halls[v] = v번 전시실과 복도로 이어진 전시실들(양쪽으로 다닌다)const halls: number[][] = Array.from({ length: n + 1 }, (): number[] => []);for (let i = 0; i < m; i++) { const a = data[2 + n + 2 * i]; const b = data[3 + n + 2 * i]; halls[a].push(b); halls[b].push(a);}// 너비 우선 탐색(BFS): 큐에서 꺼내는 차례가 곧 도착 시각이 이른 차례다const arrive: number[] = new Array<number>(n + 1).fill(-1);arrive[1] = 0; // 1번에는 처음부터 있으니 close[1]은 보지 않는다const queue: number[] = [1];// shift()는 뒤의 것을 모두 당겨서 느리다. 머리 번호 head를 옮긴다for (let head = 0; head < queue.length; head++) { const u = queue[head]; const t = arrive[u] + 1; // 이웃 전시실에 닿는 시각 for (const v of halls[u]) { // 처음 닿는 때가 가장 이른 때다. 그때 이미 잠겼으면 나중에 와도 잠겨 있다 if (arrive[v] === -1 && t <= close[v]) { arrive[v] = t; queue.push(v); } }}console.log(queue.length); // 큐에 한 번씩 들어간 전시실이 곧 들어갈 수 있는 전시실이다console.log(arrive.slice(1).join(" "));
JavaMain.java · 54 줄
import java.io.*;import java.util.*;public class Main { public static void main(String[] args) throws IOException { StreamTokenizer st = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in))); st.nextToken(); int n = (int) st.nval; st.nextToken(); int m = (int) st.nval; int[] close = new int[n + 1]; // close[v] = v번 전시실에 들어갈 수 있는 마지막 시각 for (int v = 1; v <= n; v++) { st.nextToken(); close[v] = (int) st.nval; } // 인접 목록: halls.get(v) = v번 전시실과 복도로 이어진 전시실들(양쪽으로 다닌다) List<List<Integer>> halls = new ArrayList<>(); for (int v = 0; v <= n; v++) halls.add(new ArrayList<>()); for (int i = 0; i < m; i++) { st.nextToken(); int a = (int) st.nval; st.nextToken(); int b = (int) st.nval; halls.get(a).add(b); halls.get(b).add(a); } // 너비 우선 탐색(BFS): 큐에서 꺼내는 차례가 곧 도착 시각이 이른 차례다 int[] arrive = new int[n + 1]; Arrays.fill(arrive, -1); arrive[1] = 0; // 1번에는 처음부터 있으니 close[1]은 보지 않는다 ArrayDeque<Integer> queue = new ArrayDeque<>(); queue.add(1); int reached = 1; while (!queue.isEmpty()) { int u = queue.poll(); int t = arrive[u] + 1; // 이웃 전시실에 닿는 시각 for (int v : halls.get(u)) { // 처음 닿는 때가 가장 이른 때다. 그때 이미 잠겼으면 나중에 와도 잠겨 있다 if (arrive[v] == -1 && t <= close[v]) { arrive[v] = t; queue.add(v); reached++; } } } StringBuilder sb = new StringBuilder(); sb.append(reached).append('\n'); for (int v = 1; v <= n; v++) sb.append(arrive[v]).append(v < n ? ' ' : '\n'); System.out.print(sb); }}
C#Main.cs · 52 줄
using System;using System.Collections.Generic;using System.Text;class Program{ static void Main() { var data = Console.In.ReadToEnd().Split((char[]?)null, StringSplitOptions.RemoveEmptyEntries); int n = int.Parse(data[0]), m = int.Parse(data[1]); var close = new int[n + 1]; // close[v] = v번 전시실에 들어갈 수 있는 마지막 시각 for (int v = 1; v <= n; v++) close[v] = int.Parse(data[1 + v]); // 인접 목록: halls[v] = v번 전시실과 복도로 이어진 전시실들(양쪽으로 다닌다) var halls = new List<int>[n + 1]; for (int v = 0; v <= n; v++) halls[v] = new List<int>(); for (int i = 0; i < m; i++) { int a = int.Parse(data[2 + n + 2 * i]), b = int.Parse(data[3 + n + 2 * i]); halls[a].Add(b); halls[b].Add(a); } // 너비 우선 탐색(BFS): 큐에서 꺼내는 차례가 곧 도착 시각이 이른 차례다 var arrive = new int[n + 1]; Array.Fill(arrive, -1); arrive[1] = 0; // 1번에는 처음부터 있으니 close[1]은 보지 않는다 var queue = new Queue<int>(); queue.Enqueue(1); int reached = 1; while (queue.Count > 0) { int u = queue.Dequeue(); int t = arrive[u] + 1; // 이웃 전시실에 닿는 시각 foreach (int v in halls[u]) { // 처음 닿는 때가 가장 이른 때다. 그때 이미 잠겼으면 나중에 와도 잠겨 있다 if (arrive[v] == -1 && t <= close[v]) { arrive[v] = t; queue.Enqueue(v); reached++; } } } var sb = new StringBuilder(); sb.Append(reached).Append('\n'); for (int v = 1; v <= n; v++) sb.Append(arrive[v]).Append(v < n ? ' ' : '\n'); Console.Write(sb); }}
C++main.cpp · 46 줄
#include <iostream>#include <queue>#include <vector>using namespace std;int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n, m; cin >> n >> m; vector<int> close(n + 1); // close[v] = v번 전시실에 들어갈 수 있는 마지막 시각 for (int v = 1; v <= n; v++) cin >> close[v]; // 인접 목록: halls[v] = v번 전시실과 복도로 이어진 전시실들(양쪽으로 다닌다) vector<vector<int>> halls(n + 1); for (int i = 0; i < m; i++) { int a, b; cin >> a >> b; halls[a].push_back(b); halls[b].push_back(a); } // 너비 우선 탐색(BFS): 큐에서 꺼내는 차례가 곧 도착 시각이 이른 차례다 vector<int> arrive(n + 1, -1); arrive[1] = 0; // 1번에는 처음부터 있으니 close[1]은 보지 않는다 queue<int> q; q.push(1); int reached = 1; while (!q.empty()) { int u = q.front(); q.pop(); int t = arrive[u] + 1; // 이웃 전시실에 닿는 시각 for (int v : halls[u]) { // 처음 닿는 때가 가장 이른 때다. 그때 이미 잠겼으면 나중에 와도 잠겨 있다 if (arrive[v] == -1 && t <= close[v]) { arrive[v] = t; q.push(v); reached++; } } } cout << reached << '\n'; for (int v = 1; v <= n; v++) cout << arrive[v] << (v < n ? ' ' : '\n'); return 0;}
Cmain.c · 52 줄
#include <stdio.h>#include <stdlib.h>int main(void) { int n, m; if (scanf("%d %d", &n, &m) != 2) return 0; int *close = malloc((n + 1) * sizeof(int)); // close[v] = v번 전시실에 들어갈 수 있는 마지막 시각 for (int v = 1; v <= n; v++) scanf("%d", &close[v]); // 인접 목록을 배열 셋으로: head[v]에서 시작해 next[e]로 따라가며 to[e]를 본다 // 복도 하나가 양쪽으로 다니는 길 둘이 되므로 칸이 2m개 든다 int *head = malloc((n + 1) * sizeof(int)); int *next = malloc((2 * m + 1) * sizeof(int)); int *to = malloc((2 * m + 1) * sizeof(int)); for (int v = 0; v <= n; v++) head[v] = -1; for (int i = 0; i < m; i++) { int a, b; scanf("%d %d", &a, &b); to[2 * i] = b; next[2 * i] = head[a]; head[a] = 2 * i; to[2 * i + 1] = a; next[2 * i + 1] = head[b]; head[b] = 2 * i + 1; } // 너비 우선 탐색(BFS): 큐에서 꺼내는 차례가 곧 도착 시각이 이른 차례다 int *arrive = malloc((n + 1) * sizeof(int)); int *queue = malloc((n + 1) * sizeof(int)); // 전시실마다 한 번만 들어가니 n칸이면 된다 for (int v = 0; v <= n; v++) arrive[v] = -1; arrive[1] = 0; // 1번에는 처음부터 있으니 close[1]은 보지 않는다 int qh = 0, qt = 0; queue[qt++] = 1; while (qh < qt) { int u = queue[qh++]; int t = arrive[u] + 1; // 이웃 전시실에 닿는 시각 for (int e = head[u]; e != -1; e = next[e]) { int v = to[e]; // 처음 닿는 때가 가장 이른 때다. 그때 이미 잠겼으면 나중에 와도 잠겨 있다 if (arrive[v] == -1 && t <= close[v]) { arrive[v] = t; queue[qt++] = v; } } } printf("%d\n", qt); // 큐에 한 번씩 들어간 전시실의 수 for (int v = 1; v <= n; v++) printf(v > 1 ? " %d" : "%d", arrive[v]); printf("\n"); free(close); free(head); free(next); free(to); free(arrive); free(queue); return 0;}
Craykonmain.cray · 44 줄
첫줄 = 콘솔.수들읽기();n = 첫줄[0];m = 첫줄[1];// 목록은 10,000칸까지라 전시실 번호에서 1을 빼 0번부터 둔다// close[v] = v번 전시실에 들어갈 수 있는 마지막 시각close = 콘솔.수들읽기();// 인접 목록: halls[v] = v번 전시실과 복도로 이어진 전시실들(양쪽으로 다닌다)halls = [];반복 (v = 0; v < n; v++){ halls.넣기([]);}반복 (i = 0; i < m; i++){ 쌍 = 콘솔.수들읽기(); halls[쌍[0] - 1].넣기(쌍[1] - 1); halls[쌍[1] - 1].넣기(쌍[0] - 1);}// 너비 우선 탐색(BFS): 큐에서 꺼내는 차례가 곧 도착 시각이 이른 차례다arrive = 새목록(n, -1);arrive[0] = 0; // 1번에는 처음부터 있으니 close는 보지 않는다queue = [0];// 앞에서 빼는 대신 머리 번호 head를 옮긴다head = 0;동안 (head < queue.개수){ u = queue[head]; head++; t = arrive[u] + 1; // 이웃 전시실에 닿는 시각 각각 (v 에서 halls[u]) { // 처음 닿는 때가 가장 이른 때다. 그때 이미 잠겼으면 나중에 와도 잠겨 있다 만약 (arrive[v] == -1 && t <= close[v]) { arrive[v] = t; queue.넣기(v); } }}콘솔.쓰기(queue.개수); // 큐에 한 번씩 들어간 전시실의 수콘솔.쓰기(arrive);