{ }codelper
깊이 우선 탐색그래프(인접 목록)P0189

버전 범위를 맞추는 꾸러미 설치기

꾸러미 저장소에 꾸러미 버전 n개가 있다. 꾸러미 버전 하나는 이름과 버전 번호로 정해지고, 그 버전을 쓰려면 먼저 있어야 하는 요구 몇 개를 가진다. 요구 하나 'b lo hi'는 이름이 b이고 버전이 lo 이상 hi 이하인 꾸러미가 있어야 한다는 뜻이다. 맨 처음 설치하라고 받은 요구 목록이 주어진다. 설치기는 받은 요구를 차례로 하나씩 아래 규칙으로 푼다. 요구 (b, lo, hi)를 풀 때: 1.
b가 지금 풀고 있는 중이면(아래 3에서 b의 요구들을 아직 다 풀지 못했으면) 'CYCLE b'로 멈춘다. 2.
그렇지 않고 b의 버전을 이미 골랐으면, 고른 버전이 lo 이상 hi 이하이면 이 요구는 끝이고, 아니면 'CONFLICT b'로 멈춘다.
한 번 고른 버전은 바꾸지 않는다. 3.
b를 아직 고르지 않았으면, 저장소에 있는 b의 버전 가운데 lo 이상 hi 이하인 것 중 가장 높은 버전을 고른다.
그런 버전이 없으면(이름 b가 저장소에 아예 없어도) 'MISSING b'로 멈춘다. 고른 버전의 요구들을 적힌 차례대로 같은 규칙으로 모두 푼 다음, 그 꾸러미 버전을 설치 차례의 맨 끝에 붙인다. 멈추는 일 없이 맨 처음 요구들을 모두 풀면 설치 차례를 한 줄에 하나씩 '이름 버전'으로 출력하라. 도중에 멈추면 처음 멈춘 까닭 한 줄만 출력한다.

입력
첫 줄에 n, 다음 n줄에 줄마다 '이름 버전 d'와 요구 d개가 'b lo hi' 꼴로 빈칸으로 이어서 주어진다(꾸러미 줄은 아무 차례로나 온다).
마지막 줄에 맨 처음 요구 수 r과 요구 r개가 같은 꼴로 주어진다.
1 ≤ n ≤ 3,000, 1 ≤ r, 모든 d와 r의 합 ≤ 10,000, 1 ≤ 버전, lo ≤ hi ≤ 1,000,000.
이름은 영어 소문자 1~10글자이고, 꾸러미 줄에 나오는 서로 다른 이름은 1,500개 이하이며, 같은 이름과 버전의 꾸러미 줄은 두 번 나오지 않는다.
출력
설치 차례(줄마다 '이름 버전'), 또는 'CYCLE 이름', 'CONFLICT 이름', 'MISSING 이름' 가운데 한 줄
예시 1 · stdin
6
app 2 2 web 1 3 db 2 5
app 1 0
web 3 1 db 1 4
web 2 1 db 1 9
db 4 0
db 6 0
1 app 1 9
stdout
db 4
web 3
app 2
예시 2 · stdin
6
app 2 2 web 1 3 db 2 5
app 1 0
web 3 1 db 1 4
web 2 1 db 1 9
db 4 0
db 6 0
2 app 1 9 db 5 9
stdout
CONFLICT db
main.py
입력(stdin)

실행하면 이 줄들이 차례로 input()·표준 입력으로 들어가요(실행 중에 묻지 않아요).

$ 준비됐어요

코드를 입력해 풀려면 PC에서 열어 주세요. 모바일에서는 문제와 풀이를 볼 수 있어요.

다른 언어로는?언어별 풀이 보기 · 8개 언어 풀이

꾸러미와 요구는 방향 있는 그래프이고, 설치기는 그 그래프를 깊이 우선 탐색(DFS)으로 걸어요.
꾸러미마다 상태가 셋이에요: 아직 안 봄, 풀고 있는 중(지금 길 위), 다 끝남.
의존을 다 푼 뒤에 설치 차례에 넣으니(후위 순서) 의존이 언제나 먼저 설치돼요.
고리는 '풀고 있는 중'인 꾸러미를 다시 만날 때만 생겨요 — 이미 고른 이름을 모두 고리로 치면 두 꾸러미가 같은 꾸러미를 요구하는 다이아몬드 모양도 고리로 잘못 봐요.
그래서 다 풀고 나면 길 위 표시를 꼭 지워요.
반대로 길 위인지 보지 않고 '이미 골랐으니 끝'으로 치면 고리를 놓쳐요.
다른 함정: 설치 차례에 넣는 때를 의존보다 먼저 하면(전위 순서) 차례가 뒤집히고, 가장 낮은 버전을 고르면 답이 달라져요(예시 1에서는 app 2 대신 app 1을 골라 다른 꾸러미를 하나도 설치하지 않아요).
깊이도 함정이에요: 이름이 1,500개까지라 사슬이 1,500단계까지 깊어질 수 있어서, Python은 기본 재귀 한도(1,000)를 넘어 RecursionError가 나요 — sys.setrecursionlimit로 늘리거나 스택으로 바꿔요.
Java·C#·C·C++·JavaScript의 기본 호출 스택은 이 정도 깊이를 견디고, 크레이콘은 몇천 단계까지 부를 수 있어서 1,500단계는 괜찮아요.
오류를 바깥으로 알리는 법도 달라요: Python은 예외(raise/except)로 한 번에 빠져나오고, Java·C#·JavaScript·TypeScript는 오류 문자열 아니면 null을 돌려주며(C#은 string?, TypeScript는 string | null), C++은 std::optional<std::string>, C는 성공 여부(1/0)를 돌려주고 오류 문장은 전역 버퍼에 적어요.
크레이콘에는 예외가 없어서 빈 문자열 ""이면 문제없음으로 정하고 돌려준 값을 매번 확인해요.
표는 Python dict/set, Java HashMap/HashSet(computeIfAbsent로 목록 만들기), C# Dictionary의 튜플 열쇠 (string, int), C++ std::unordered_map과 std::map<std::pair<std::string, int>, …>, C는 정렬한 이름 배열과 bsearch, JavaScript·TypeScript는 Map/Set, 크레이콘은 사전()의 열쇠를 이름 + " " + 버전 문자열로 만들고 길 위 표시는 onPath.빼기(이름)으로 지워요.

Pythonmain.py · 56 줄
import sys sys.setrecursionlimit(10000)  # 의존 사슬이 1,500단계까지 깊어질 수 있다(기본 한도는 1,000)  class ResolveError(Exception):    pass  def read_reqs(tokens, count, start):    """tokens[start:]에서 (이름, lo, hi) count개를 읽는다."""    return [(tokens[start + 3 * i], int(tokens[start + 3 * i + 1]), int(tokens[start + 3 * i + 2])) for i in range(count)]  lines = sys.stdin.read().split("\n")n = int(lines[0])versions = {}  # 이름 → 있는 버전들deps = {}      # (이름, 버전) → 그 버전이 요구하는 것들for line in lines[1:1 + n]:    t = line.split()    name, ver, d = t[0], int(t[1]), int(t[2])    versions.setdefault(name, []).append(ver)    deps[(name, ver)] = read_reqs(t, d, 3)root = lines[1 + n].split()root_reqs = read_reqs(root, int(root[0]), 1) chosen = {}     # 이름 → 고른 버전on_path = set() # 지금 의존을 풀고 있는 중인 이름(깊이 우선 탐색의 길 위)order = []      # 설치 차례: 의존이 모두 끝난 뒤에 넣는다(후위 순서)  def resolve(name, lo, hi):    if name in on_path:        raise ResolveError(f"CYCLE {name}")    if name in chosen:        if not lo <= chosen[name] <= hi:            raise ResolveError(f"CONFLICT {name}")        return    fits = [v for v in versions.get(name, []) if lo <= v <= hi]    if not fits:        raise ResolveError(f"MISSING {name}")    ver = max(fits)    chosen[name] = ver    on_path.add(name)    for dep, dlo, dhi in deps[(name, ver)]:        resolve(dep, dlo, dhi)    on_path.remove(name)    order.append(f"{name} {ver}")  try:    for name, lo, hi in root_reqs:        resolve(name, lo, hi)    print("\n".join(order))except ResolveError as e:    print(e)
JavaScriptmain.js · 54 줄
const lines = require("fs").readFileSync(0, "utf8").split("\n"); // tokens[start..]에서 [이름, lo, hi] count개를 읽는다function readReqs(tokens, count, start) {  const reqs = [];  for (let i = 0; i < count; i++)    reqs.push([tokens[start + 3 * i], Number(tokens[start + 3 * i + 1]), Number(tokens[start + 3 * i + 2])]);  return reqs;} const n = Number(lines[0]);const versions = new Map(); // 이름 → 있는 버전들const deps = new Map();     // "이름 버전" → 그 버전이 요구하는 것들for (let i = 1; i <= n; i++) {  const t = lines[i].trim().split(/\s+/);  const name = t[0], ver = Number(t[1]);  if (!versions.has(name)) versions.set(name, []);  versions.get(name).push(ver);  deps.set(`${name} ${ver}`, readReqs(t, Number(t[2]), 3));}const root = lines[n + 1].trim().split(/\s+/);const rootReqs = readReqs(root, Number(root[0]), 1); const chosen = new Map();  // 이름 → 고른 버전const onPath = new Set();  // 지금 의존을 풀고 있는 중인 이름(깊이 우선 탐색의 길 위)const order = [];          // 설치 차례: 의존이 모두 끝난 뒤에 넣는다(후위 순서) // 문제가 없으면 null, 있으면 오류 줄을 돌려준다function resolve(name, lo, hi) {  if (onPath.has(name)) return `CYCLE ${name}`;  if (chosen.has(name)) {    const v = chosen.get(name);    return lo <= v && v <= hi ? null : `CONFLICT ${name}`;  }  const fits = (versions.get(name) ?? []).filter((v) => lo <= v && v <= hi);  if (fits.length === 0) return `MISSING ${name}`;  const ver = Math.max(...fits);  chosen.set(name, ver);  onPath.add(name);  for (const [dep, dlo, dhi] of deps.get(`${name} ${ver}`)) {    const err = resolve(dep, dlo, dhi);    if (err !== null) return err;  }  onPath.delete(name);  order.push(`${name} ${ver}`);  return null;} let error = null;for (const [name, lo, hi] of rootReqs) {  error = resolve(name, lo, hi);  if (error !== null) break;}console.log(error ?? order.join("\n"));
TypeScriptmain.ts · 57 줄
import { readFileSync } from "node:fs"; type Req = [string, number, number];const lines: string[] = readFileSync(0, "utf8").split("\n"); // tokens[start..]에서 [이름, lo, hi] count개를 읽는다function readReqs(tokens: string[], count: number, start: number): Req[] {  const reqs: Req[] = [];  for (let i = 0; i < count; i++)    reqs.push([tokens[start + 3 * i], Number(tokens[start + 3 * i + 1]), Number(tokens[start + 3 * i + 2])]);  return reqs;} const n = Number(lines[0]);const versions = new Map<string, number[]>(); // 이름 → 있는 버전들const deps = new Map<string, Req[]>(); // "이름 버전" → 그 버전이 요구하는 것들for (let i = 1; i <= n; i++) {  const t = lines[i].trim().split(/\s+/);  const name = t[0], ver = Number(t[1]);  if (!versions.has(name)) versions.set(name, []);  versions.get(name)!.push(ver);  deps.set(`${name} ${ver}`, readReqs(t, Number(t[2]), 3));}const root = lines[n + 1].trim().split(/\s+/);const rootReqs = readReqs(root, Number(root[0]), 1); const chosen = new Map<string, number>(); // 이름 → 고른 버전const onPath = new Set<string>(); // 지금 의존을 풀고 있는 중인 이름(깊이 우선 탐색의 길 위)const order: string[] = []; // 설치 차례: 의존이 모두 끝난 뒤에 넣는다(후위 순서) // 문제가 없으면 null, 있으면 오류 줄을 돌려준다function resolve(name: string, lo: number, hi: number): string | null {  if (onPath.has(name)) return `CYCLE ${name}`;  if (chosen.has(name)) {    const v = chosen.get(name)!;    return lo <= v && v <= hi ? null : `CONFLICT ${name}`;  }  const fits = (versions.get(name) ?? []).filter((v) => lo <= v && v <= hi);  if (fits.length === 0) return `MISSING ${name}`;  const ver = Math.max(...fits);  chosen.set(name, ver);  onPath.add(name);  for (const [dep, dlo, dhi] of deps.get(`${name} ${ver}`)!) {    const err = resolve(dep, dlo, dhi);    if (err !== null) return err;  }  onPath.delete(name);  order.push(`${name} ${ver}`);  return null;} let error: string | null = null;for (const [name, lo, hi] of rootReqs) {  error = resolve(name, lo, hi);  if (error !== null) break;}console.log(error ?? order.join("\n"));
JavaMain.java · 65 줄
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.util.ArrayList;import java.util.HashMap;import java.util.HashSet;import java.util.List;import java.util.Map;import java.util.Set; public class Main {    record Req(String name, int lo, int hi) {}     static Map<String, List<Integer>> versions = new HashMap<>(); // 이름 → 있는 버전들    static Map<String, List<Req>> deps = new HashMap<>();        // "이름 버전" → 그 버전이 요구하는 것들    static Map<String, Integer> chosen = new HashMap<>();        // 이름 → 고른 버전    static Set<String> onPath = new HashSet<>();                 // 지금 의존을 풀고 있는 중인 이름(깊이 우선 탐색의 길 위)    static StringBuilder order = new StringBuilder();           // 설치 차례: 의존이 모두 끝난 뒤에 넣는다(후위 순서)     static List<Req> readReqs(String[] t, int count, int start) {        List<Req> reqs = new ArrayList<>();        for (int i = 0; i < count; i++)            reqs.add(new Req(t[start + 3 * i], Integer.parseInt(t[start + 3 * i + 1]), Integer.parseInt(t[start + 3 * i + 2])));        return reqs;    }     // 문제가 없으면 null, 있으면 오류 줄을 돌려준다    static String resolve(Req req) {        String name = req.name();        if (onPath.contains(name)) return "CYCLE " + name;        Integer have = chosen.get(name);        if (have != null) return req.lo() <= have && have <= req.hi() ? null : "CONFLICT " + name;        int ver = -1;        for (int v : versions.getOrDefault(name, List.of()))            if (req.lo() <= v && v <= req.hi()) ver = Math.max(ver, v);        if (ver == -1) return "MISSING " + name;        chosen.put(name, ver);        onPath.add(name);        for (Req dep : deps.get(name + " " + ver)) {            String err = resolve(dep);            if (err != null) return err;        }        onPath.remove(name);        order.append(name).append(' ').append(ver).append('\n');        return null;    }     public static void main(String[] args) throws IOException {        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));        int n = Integer.parseInt(in.readLine().trim());        for (int i = 0; i < n; i++) {            String[] t = in.readLine().trim().split("\\s+");            int ver = Integer.parseInt(t[1]);            versions.computeIfAbsent(t[0], key -> new ArrayList<>()).add(ver);            deps.put(t[0] + " " + ver, readReqs(t, Integer.parseInt(t[2]), 3));        }        String[] root = in.readLine().trim().split("\\s+");        String error = null;        for (Req req : readReqs(root, Integer.parseInt(root[0]), 1)) {            error = resolve(req);            if (error != null) break;        }        System.out.print(error != null ? error + "\n" : order);    }}
C#Main.cs · 63 줄
using System;using System.Collections.Generic;using System.Linq;using System.Text; class Program{    record Req(string Name, int Lo, int Hi);     static readonly Dictionary<string, List<int>> versions = new(); // 이름 → 있는 버전들    static readonly Dictionary<(string, int), List<Req>> deps = new(); // (이름, 버전) → 그 버전이 요구하는 것들    static readonly Dictionary<string, int> chosen = new();         // 이름 → 고른 버전    static readonly HashSet<string> onPath = new();                 // 지금 의존을 풀고 있는 중인 이름(깊이 우선 탐색의 길 위)    static readonly StringBuilder order = new();                    // 설치 차례: 의존이 모두 끝난 뒤에 넣는다(후위 순서)     static List<Req> ReadReqs(string[] t, int count, int start) =>        Enumerable.Range(0, count)            .Select(i => new Req(t[start + 3 * i], int.Parse(t[start + 3 * i + 1]), int.Parse(t[start + 3 * i + 2])))            .ToList();     // 문제가 없으면 null, 있으면 오류 줄을 돌려준다    static string? Resolve(Req req)    {        string name = req.Name;        if (onPath.Contains(name)) return "CYCLE " + name;        if (chosen.TryGetValue(name, out int have))            return req.Lo <= have && have <= req.Hi ? null : "CONFLICT " + name;        var fits = versions.GetValueOrDefault(name, new List<int>()).Where(v => req.Lo <= v && v <= req.Hi).ToList();        if (fits.Count == 0) return "MISSING " + name;        int ver = fits.Max();        chosen[name] = ver;        onPath.Add(name);        foreach (var dep in deps[(name, ver)])        {            string? err = Resolve(dep);            if (err != null) return err;        }        onPath.Remove(name);        order.Append(name).Append(' ').Append(ver).Append('\n');        return null;    }     static void Main()    {        int n = int.Parse(Console.ReadLine()!.Trim());        for (int i = 0; i < n; i++)        {            string[] t = Console.ReadLine()!.Split(' ', StringSplitOptions.RemoveEmptyEntries);            int ver = int.Parse(t[1]);            if (!versions.ContainsKey(t[0])) versions[t[0]] = new List<int>();            versions[t[0]].Add(ver);            deps[(t[0], ver)] = ReadReqs(t, int.Parse(t[2]), 3);        }        string[] root = Console.ReadLine()!.Split(' ', StringSplitOptions.RemoveEmptyEntries);        string? error = null;        foreach (var req in ReadReqs(root, int.Parse(root[0]), 1))        {            error = Resolve(req);            if (error != null) break;        }        Console.Write(error != null ? error + "\n" : order.ToString());    }}
C++main.cpp · 73 줄
#include <iostream>#include <map>#include <optional>#include <set>#include <string>#include <unordered_map>#include <unordered_set>#include <vector> struct Req {    std::string name;    int lo, hi;}; std::unordered_map<std::string, std::vector<int>> versions;      // 이름 → 있는 버전들std::map<std::pair<std::string, int>, std::vector<Req>> deps;    // (이름, 버전) → 그 버전이 요구하는 것들std::unordered_map<std::string, int> chosen;                     // 이름 → 고른 버전std::unordered_set<std::string> onPath;                          // 지금 의존을 풀고 있는 중인 이름(깊이 우선 탐색의 길 위)std::vector<std::pair<std::string, int>> order;                  // 설치 차례: 의존이 모두 끝난 뒤에 넣는다(후위 순서) std::vector<Req> readReqs(std::istream& in, int count) {    std::vector<Req> reqs(count);    for (auto& r : reqs) in >> r.name >> r.lo >> r.hi;    return reqs;} // 문제가 없으면 빈 값, 있으면 오류 줄을 돌려준다std::optional<std::string> resolve(const Req& req) {    const std::string& name = req.name;    if (onPath.count(name)) return "CYCLE " + name;    if (auto it = chosen.find(name); it != chosen.end()) {        if (req.lo <= it->second && it->second <= req.hi) return std::nullopt;        return "CONFLICT " + name;    }    int ver = -1;    if (auto it = versions.find(name); it != versions.end())        for (int v : it->second)            if (req.lo <= v && v <= req.hi && v > ver) ver = v;    if (ver == -1) return "MISSING " + name;    chosen[name] = ver;    onPath.insert(name);    for (const Req& dep : deps[{name, ver}])        if (auto err = resolve(dep)) return err;    onPath.erase(name);    order.emplace_back(name, ver);    return std::nullopt;} int main() {    std::ios::sync_with_stdio(false);    std::cin.tie(nullptr);    int n;    std::cin >> n;    for (int i = 0; i < n; i++) {        std::string name;        int ver, d;        std::cin >> name >> ver >> d;        versions[name].push_back(ver);        deps[{name, ver}] = readReqs(std::cin, d);    }    int r;    std::cin >> r;    std::optional<std::string> error;    for (const Req& req : readReqs(std::cin, r)) {        error = resolve(req);        if (error) break;    }    if (error) {        std::cout << *error << "\n";    } else {        for (const auto& [name, ver] : order) std::cout << name << " " << ver << "\n";    }}
Cmain.c · 90 줄
#include <stdio.h>#include <stdlib.h>#include <string.h> #define MAXN 3000#define MAXREQ 10000 typedef struct {    char name[11];    int ver;    int first, count; /* 이 버전의 요구들: reqs[first .. first + count - 1] */} Pkg; typedef struct {    char name[11];    int lo, hi;} Req; static Pkg pkgs[MAXN];static Req reqs[MAXREQ];static int n, nreq; /* 이름마다 하나: 고른 버전(없으면 0)과 길 위에 있는지 */static char names[MAXN][11];static int chosen[MAXN], onPath[MAXN], nnames;static int orderPkg[MAXN], norder; /* 설치 차례(후위 순서) */static char error[32]; static int byName(const void *a, const void *b) {    return strcmp((const char *)a, (const char *)b);} /* 이름 번호(없으면 -1): names는 정렬해 두었으니 이진 탐색 */static int nameId(const char *name) {    char(*p)[11] = bsearch(name, names, nnames, sizeof names[0], byName);    return p == NULL ? -1 : (int)(p - names);} /* 문제가 없으면 1, 있으면 error에 적고 0 */static int resolve(const Req *req) {    int id = nameId(req->name);    if (id >= 0 && onPath[id]) { sprintf(error, "CYCLE %s", req->name); return 0; }    if (id >= 0 && chosen[id] != 0) {        if (req->lo <= chosen[id] && chosen[id] <= req->hi) return 1;        sprintf(error, "CONFLICT %s", req->name);        return 0;    }    int best = -1;    for (int i = 0; i < n; i++)        if (strcmp(pkgs[i].name, req->name) == 0 && req->lo <= pkgs[i].ver && pkgs[i].ver <= req->hi &&            (best == -1 || pkgs[i].ver > pkgs[best].ver))            best = i;    if (best == -1) { sprintf(error, "MISSING %s", req->name); return 0; }    chosen[id] = pkgs[best].ver;    onPath[id] = 1;    for (int j = 0; j < pkgs[best].count; j++)        if (!resolve(&reqs[pkgs[best].first + j])) return 0;    onPath[id] = 0;    orderPkg[norder++] = best;    return 1;} int main(void) {    scanf("%d", &n);    for (int i = 0; i < n; i++) {        scanf("%10s %d %d", pkgs[i].name, &pkgs[i].ver, &pkgs[i].count);        pkgs[i].first = nreq;        for (int j = 0; j < pkgs[i].count; j++, nreq++)            scanf("%10s %d %d", reqs[nreq].name, &reqs[nreq].lo, &reqs[nreq].hi);        strcpy(names[i], pkgs[i].name);    }    /* 이름을 정렬하고 겹치는 것을 없앤다 */    qsort(names, n, sizeof names[0], byName);    for (int i = 0; i < n; i++)        if (nnames == 0 || strcmp(names[nnames - 1], names[i]) != 0) {            if (nnames != i) strcpy(names[nnames], names[i]); /* 같은 칸끼리 strcpy하면 안 된다 */            nnames++;        }    int r, rootFirst = nreq;    scanf("%d", &r);    for (int j = 0; j < r; j++, nreq++) scanf("%10s %d %d", reqs[nreq].name, &reqs[nreq].lo, &reqs[nreq].hi);    int ok = 1;    for (int j = 0; j < r && ok; j++) ok = resolve(&reqs[rootFirst + j]);    if (!ok) {        puts(error);    } else {        for (int i = 0; i < norder; i++) printf("%s %d\n", pkgs[orderPkg[i]].name, pkgs[orderPkg[i]].ver);    }    return 0;}
Craykonmain.cray · 97 줄
// 요구 하나는 [이름, lo, hi]요구읽기(tokens, count, start){    reqs = [];    반복 (i = 0; i < count; i++)    {        reqs.넣기([tokens[start + 3 * i], tokens[start + 3 * i + 1].수로변환(), tokens[start + 3 * i + 2].수로변환()]);    }    돌려주기 reqs;} versions = 사전(); // 이름 → 있는 버전들deps = 사전(); // "이름 버전" → 그 버전이 요구하는 것들n = 콘솔.줄읽기().공백제거().수로변환();반복 (i = 0; i < n; i++){    t = 콘솔.줄읽기().공백제거().나누기(" ");    name = t[0];    ver = t[1].수로변환();    만약 (!versions.있나(name))    {        versions[name] = [];    }    versions[name].넣기(ver);    deps[name + " " + ver] = 요구읽기(t, t[2].수로변환(), 3);}root = 콘솔.줄읽기().공백제거().나누기(" ");rootReqs = 요구읽기(root, root[0].수로변환(), 1); chosen = 사전(); // 이름 → 고른 버전onPath = 사전(); // 지금 의존을 풀고 있는 중인 이름(깊이 우선 탐색의 길 위)order = []; // 설치 차례: 의존이 모두 끝난 뒤에 넣는다(후위 순서) // 문제가 없으면 "", 있으면 오류 줄을 돌려준다풀기(req){    이름 = req[0];    lo = req[1];    hi = req[2];    만약 (onPath.있나(이름))    {        돌려주기 "CYCLE " + 이름;    }    만약 (chosen.있나(이름))    {        만약 (lo <= chosen[이름] && chosen[이름] <= hi)        {            돌려주기 "";        }        돌려주기 "CONFLICT " + 이름;    }    고른 = -1;    만약 (versions.있나(이름))    {        각각 (v 에서 versions[이름])        {            만약 (lo <= v && v <= hi && v > 고른)            {                고른 = v;            }        }    }    만약 (고른 == -1)    {        돌려주기 "MISSING " + 이름;    }    chosen[이름] = 고른;    onPath[이름] = 참;    각각 (dep 에서 deps[이름 + " " + 고른])    {        err = 풀기(dep);        만약 (err != "")        {            돌려주기 err;        }    }    onPath.빼기(이름);    order.넣기(이름 + " " + 고른);    돌려주기 "";} error = "";각각 (req 에서 rootReqs){    만약 (error == "")    {        error = 풀기(req);    }}만약 (error != ""){    콘솔.쓰기(error);}아니면{    콘솔.쓰기(order.잇기("\n"));}