{ }codelper
열거형switchP0156

주문 상태 기계

온라인 가게에 주문 n개가 있고, 1번부터 n번까지 모두 CREATED 상태에서 시작한다. 주문의 상태는 CREATED, PAID, SHIPPED, DELIVERED, CANCELLED, REFUNDED, RETURNED 일곱 가지다. 사건은 pay, ship, deliver, cancel, return 다섯 가지이고, 상태는 다음 여섯 경우에만 바뀐다. - CREATED에서 pay → PAID - CREATED에서 cancel → CANCELLED - PAID에서 ship → SHIPPED - PAID에서 cancel → REFUNDED - SHIPPED에서 deliver → DELIVERED - DELIVERED에서 return → RETURNED 그 밖의 (상태, 사건) 짝은 모두 거절되고, 그 주문의 상태는 그대로다. CANCELLED, REFUNDED, RETURNED에서는 어떤 사건도 받지 않는다. 첫 줄에 n과 사건 수 q, 다음 q줄에 "주문번호 사건"이 일어난 차례대로 주어진다. 1번부터 n번 주문의 마지막 상태를 한 줄에 하나씩 대문자 이름으로 출력하고, 마지막 줄에 거절된 사건의 수를 출력하라. 상태를 일곱 값을 가진 열거형(enum)으로 만들고, 지금 상태와 사건으로 다음 상태를 정하는 함수 하나로 상태 기계를 짜 보자.

입력
1 ≤ n ≤ 1,000, 1 ≤ q ≤ 10,000, 1 ≤ 주문번호 ≤ n, 사건은 pay·ship·deliver·cancel·return 중 하나
출력
n+1줄: 주문마다 상태 이름, 마지막 줄에 거절된 사건 수
예시 1 · stdin
3 6
1 pay
1 ship
2 cancel
2 pay
3 pay
3 cancel
stdout
SHIPPED
CANCELLED
REFUNDED
1
main.py
입력(stdin)

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

$ 준비됐어요

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

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

이 문제는 열거형이 있는 Python·TypeScript·Java·C#·C·C++만 받아요.
JavaScript에는 열거형 문법이 없고(Object.freeze({...})로 흉내만 내요), 크레이콘에도 열거형이 없어요.
상태 이름을 찍는 방법이 언어마다 달라요.
Python class State(Enum)의 값은 s.name이 "PAID"이고, print(s)는 State.PAID를 찍어요.
TypeScript 숫자 enum의 값은 0, 1, 2…라서 console.log(s)는 1을 찍고, 이름은 역방향 표 State[s]로 꺼내요.
Java enum은 클래스라서 toString()이 곧 이름이고, switch 식은 모든 상수를 다루지 않으면 컴파일되지 않아요.
C# enum의 ToString()은 선언한 이름 그대로(Paid)라서 이 문제는 ToUpperInvariant()로 맞췄고, (s, ev) switch { (State.Paid, "cancel") => ... }처럼 상태와 사건을 튜플 패턴으로 한 번에 맞춰요.
C enum은 이름 붙은 int 상수일 뿐이라 이름표 배열 NAME[s]를 따로 두고, C++ enum class도 이름을 몰라서 switch로 이름을 돌려줘요.
함정 넷: ① C·C++ switch에서 break를 빠뜨리면 CREATED에서 받지 못한 ship이 아래 PAID 칸으로 흘러내려 SHIPPED가 돼요.
② print(s)(Python)나 console.log(s)(TypeScript)처럼 열거형 값을 그대로 찍으면 이름이 아니라 State.PAID·1이 나와요.
③ cancel은 상태에 따라 결과가 달라요: CREATED면 CANCELLED, PAID면 REFUNDED, 그 밖에는 거절이에요.
(_, "cancel") => Cancelled처럼 상태를 보지 않고 한 줄로 쓰면 틀려요.
④ Java에서 사건 문자열을 event == "pay"로 비교하면 읽어 들인 문자열과 상수는 다른 객체라서 늘 거짓이에요.
equals로 비교해요.

Pythonmain.py · 38 줄
import sysfrom enum import Enum  class State(Enum):    CREATED = 1    PAID = 2    SHIPPED = 3    DELIVERED = 4    CANCELLED = 5    REFUNDED = 6    RETURNED = 7  # 상태 기계: (지금 상태, 사건) → 다음 상태. 표에 없는 짝은 거절NEXT = {    (State.CREATED, "pay"): State.PAID,    (State.CREATED, "cancel"): State.CANCELLED,    (State.PAID, "ship"): State.SHIPPED,    (State.PAID, "cancel"): State.REFUNDED,    (State.SHIPPED, "deliver"): State.DELIVERED,    (State.DELIVERED, "return"): State.RETURNED,} data = sys.stdin.read().split()n, q = int(data[0]), int(data[1])orders = [State.CREATED] * nrejected = 0for i in range(q):    k, event = int(data[2 + 2 * i]) - 1, data[3 + 2 * i]    nxt = NEXT.get((orders[k], event))    if nxt is None:        rejected += 1    else:        orders[k] = nxt print("\n".join(s.name for s in orders))  # print(s)는 "State.PAID"를 찍는다print(rejected)
TypeScriptmain.ts · 43 줄
import { readFileSync } from "node:fs"; enum State {  CREATED,  PAID,  SHIPPED,  DELIVERED,  CANCELLED,  REFUNDED,  RETURNED,} // 상태 기계: 받을 수 없는 사건이면 nullfunction next(s: State, event: string): State | null {  switch (s) {    case State.CREATED:      return event === "pay" ? State.PAID : event === "cancel" ? State.CANCELLED : null;    case State.PAID:      return event === "ship" ? State.SHIPPED : event === "cancel" ? State.REFUNDED : null;    case State.SHIPPED:      return event === "deliver" ? State.DELIVERED : null;    case State.DELIVERED:      return event === "return" ? State.RETURNED : null;    default:      return null; // CANCELLED·REFUNDED·RETURNED는 끝 상태  }} const words: string[] = readFileSync(0, "utf8").trim().split(/\s+/);const n = Number(words[0]);const q = Number(words[1]);const orders: State[] = new Array<State>(n).fill(State.CREATED);let rejected = 0;for (let i = 0; i < q; i++) {  const k = Number(words[2 + 2 * i]) - 1;  const ns = next(orders[k], words[3 + 2 * i]);  if (ns === null) rejected++;  else orders[k] = ns;} // 숫자 enum의 값은 0, 1, 2…: 이름은 역방향 표 State[s]로 꺼낸다console.log(orders.map((s) => State[s]).join("\n"));console.log(rejected);
JavaMain.java · 37 줄
import java.io.*;import java.util.*; public class Main {    enum State { CREATED, PAID, SHIPPED, DELIVERED, CANCELLED, REFUNDED, RETURNED }     // 상태 기계: 받을 수 없는 사건이면 null    static State next(State s, String event) {        return switch (s) {            case CREATED -> event.equals("pay") ? State.PAID : event.equals("cancel") ? State.CANCELLED : null;            case PAID -> event.equals("ship") ? State.SHIPPED : event.equals("cancel") ? State.REFUNDED : null;            case SHIPPED -> event.equals("deliver") ? State.DELIVERED : null;            case DELIVERED -> event.equals("return") ? State.RETURNED : null;            case CANCELLED, REFUNDED, RETURNED -> null; // 끝 상태        };    }     public static void main(String[] args) throws IOException {        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));        StringTokenizer st = new StringTokenizer(br.readLine());        int n = Integer.parseInt(st.nextToken()), q = Integer.parseInt(st.nextToken());        State[] orders = new State[n];        Arrays.fill(orders, State.CREATED);        int rejected = 0;        for (int i = 0; i < q; i++) {            st = new StringTokenizer(br.readLine());            int k = Integer.parseInt(st.nextToken()) - 1;            State ns = next(orders[k], st.nextToken());            if (ns == null) rejected++;            else orders[k] = ns;        }        StringBuilder sb = new StringBuilder();        for (State s : orders) sb.append(s).append('\n'); // enum의 toString()은 이름        sb.append(rejected).append('\n');        System.out.print(sb);    }}
C#Main.cs · 40 줄
using System;using System.Linq;using System.Text; enum State { Created, Paid, Shipped, Delivered, Cancelled, Refunded, Returned } class Program{    // 상태 기계: (상태, 사건) 튜플 패턴. 받을 수 없는 사건이면 null    static State? Next(State s, string ev) => (s, ev) switch    {        (State.Created, "pay") => State.Paid,        (State.Created, "cancel") => State.Cancelled,        (State.Paid, "ship") => State.Shipped,        (State.Paid, "cancel") => State.Refunded,        (State.Shipped, "deliver") => State.Delivered,        (State.Delivered, "return") => State.Returned,        _ => null,    };     static void Main()    {        var first = Console.ReadLine()!.Split(' ');        int n = int.Parse(first[0]), q = int.Parse(first[1]);        var orders = Enumerable.Repeat(State.Created, n).ToArray();        int rejected = 0;        for (int i = 0; i < q; i++)        {            var p = Console.ReadLine()!.Split(' ');            int k = int.Parse(p[0]) - 1;            State? ns = Next(orders[k], p[1]);            if (ns is State s) orders[k] = s;            else rejected++;        }        var sb = new StringBuilder();        foreach (var s in orders) sb.Append(s.ToString().ToUpperInvariant()).Append('\n'); // ToString()은 "Paid"        sb.Append(rejected).Append('\n');        Console.Write(sb);    }}
C++main.cpp · 60 줄
#include <iostream>#include <optional>#include <string>#include <vector> enum class State { Created, Paid, Shipped, Delivered, Cancelled, Refunded, Returned }; // enum class도 이름을 모른다: switch로 이름표를 만든다const char *name(State s) {    switch (s) {    case State::Created: return "CREATED";    case State::Paid: return "PAID";    case State::Shipped: return "SHIPPED";    case State::Delivered: return "DELIVERED";    case State::Cancelled: return "CANCELLED";    case State::Refunded: return "REFUNDED";    case State::Returned: return "RETURNED";    }    return "?";} // 상태 기계: 받을 수 없는 사건이면 빈 optionalstd::optional<State> next(State s, const std::string &ev) {    switch (s) {    case State::Created:        if (ev == "pay") return State::Paid;        if (ev == "cancel") return State::Cancelled;        break;    case State::Paid:        if (ev == "ship") return State::Shipped;        if (ev == "cancel") return State::Refunded;        break;    case State::Shipped:        if (ev == "deliver") return State::Delivered;        break;    case State::Delivered:        if (ev == "return") return State::Returned;        break;    default:        break;  // 끝 상태    }    return std::nullopt;} int main() {    std::ios::sync_with_stdio(false);    int n, q;    std::cin >> n >> q;    std::vector<State> orders(n, State::Created);    int rejected = 0;    for (int i = 0; i < q; i++) {        int k;        std::string ev;        std::cin >> k >> ev;        if (auto ns = next(orders[k - 1], ev)) orders[k - 1] = *ns;        else rejected++;    }    for (State s : orders) std::cout << name(s) << '\n';    std::cout << rejected << '\n';}
Cmain.c · 46 줄
#include <stdio.h>#include <string.h> enum State { CREATED, PAID, SHIPPED, DELIVERED, CANCELLED, REFUNDED, RETURNED }; /* C 열거형은 이름 붙은 int일 뿐이라 이름표를 따로 둔다(차례가 enum과 같아야 한다) */static const char *NAME[] = { "CREATED", "PAID", "SHIPPED", "DELIVERED", "CANCELLED", "REFUNDED", "RETURNED" }; /* 다음 상태를 *s에 쓰고 1, 받을 수 없는 사건이면 0 */int step(enum State *s, const char *ev) {    switch (*s) {    case CREATED:        if (strcmp(ev, "pay") == 0) { *s = PAID; return 1; }        if (strcmp(ev, "cancel") == 0) { *s = CANCELLED; return 1; }        break;                       /* break가 없으면 아래 case로 흘러내린다 */    case PAID:        if (strcmp(ev, "ship") == 0) { *s = SHIPPED; return 1; }        if (strcmp(ev, "cancel") == 0) { *s = REFUNDED; return 1; }        break;    case SHIPPED:        if (strcmp(ev, "deliver") == 0) { *s = DELIVERED; return 1; }        break;    case DELIVERED:        if (strcmp(ev, "return") == 0) { *s = RETURNED; return 1; }        break;    default:                         /* 끝 상태 */        break;    }    return 0;} int main(void) {    static enum State orders[1000];    int n, q, rejected = 0;    scanf("%d %d", &n, &q);    for (int i = 0; i < n; i++) orders[i] = CREATED;    for (int i = 0; i < q; i++) {        int k;        char ev[16];        scanf("%d %15s", &k, ev);        if (!step(&orders[k - 1], ev)) rejected++;    }    for (int i = 0; i < n; i++) puts(NAME[orders[i]]);    printf("%d\n", rejected);    return 0;}