{ }codelper
클래스P0261

카페 도장 카드

카페의 도장 카드를 클래스로 만들어라. 카드 객체는 주인 이름, 목표 도장 수 g, 지금 찍힌 도장 수, 가진 쿠폰 수를 필드로 갖는다. 생성자는 주인 이름과 g를 받고, 도장 수와 쿠폰 수는 0에서 시작한다. 음료를 한 잔 살 때마다 도장을 하나 찍는다.
찍은 뒤 도장 수가 g가 되면 도장 수는 0이 되고 쿠폰이 1장 늘어난다. 첫 줄에 명령 수 q, 다음 q줄에 명령이 하나씩 주어진다. - new 이름 g: 그 이름의 카드를 목표 g로 새로 만든다.
그 이름의 카드가 이미 있으면 옛 카드를 버리고 새 카드로 바꾼다.
새 카드의 도장 수와 쿠폰 수는 0이다.
출력은 없다. - buy 이름 k: 그 카드의 주인이 음료를 k잔 산다. - use 이름: 그 카드에 쿠폰이 있으면 1장 쓴다.
쿠폰이 없으면 아무것도 바꾸지 않는다. buy와 use를 처리할 때마다 그 카드의 "이름 도장수 쿠폰수"를 빈칸 하나씩 띄워 한 줄에 출력하라. 카드마다 객체를 하나씩 만들고, 도장 찍기·쿠폰 쓰기를 메서드로 만들어 보자.

입력
1 ≤ q ≤ 2,000.
이름은 영어 소문자·숫자 1~10글자다.
1 ≤ g ≤ 100, 1 ≤ k ≤ 100(정수).
buy와 use의 이름은 앞의 new로 이미 만들어진 카드이고, buy나 use가 하나 이상 있다
출력
buy와 use마다 한 줄씩, 명령이 나온 차례대로
예시 1 · stdin
8
new ann 3
buy ann 2
buy ann 5
use ann
new bob 1
buy bob 2
use ann
use ann
stdout
ann 2 0
ann 1 2
ann 1 1
bob 0 2
ann 1 0
ann 1 0
예시 2 · stdin
5
new kim 2
buy kim 3
new kim 5
buy kim 1
use kim
stdout
kim 1 1
kim 1 0
kim 1 0
main.py
입력(stdin)

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

$ 준비됐어요

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

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

이 문제는 클래스가 있는 Python·JavaScript·TypeScript·Java·C#·C++만 받아요(C에는 클래스가 없고, 크레이콘은 아직 클래스를 만들 수 없어요).
생성자는 Python __init__(self, owner, goal), JS·TS constructor, Java·C#·C++는 클래스와 같은 이름의 메서드예요.
TypeScript는 constructor(readonly owner: string, readonly goal: number) {}처럼 매개변수에 readonly·private를 붙이면 필드 선언과 대입을 한 번에 해 줘요.
C++는 : owner_(owner), goal_(goal) 초기화 목록을 써요.
필드에 닿는 방법도 달라요: Python은 늘 self.를 써야 하고, JS·TS는 늘 this.를 써야 해요.
Java·C#·C++는 this를 빼도 되지만, 매개변수 이름이 필드와 같으면 매개변수가 필드를 가려요.
함정 다섯: ① Java 생성자에서 goal = goal;이라 쓰면 매개변수에 자기 자신을 넣을 뿐이라 필드 goal은 0으로 남고, 도장이 영영 쿠폰이 되지 않아요(this.goal = goal;).
필드가 final이면 컴파일러가 잡아 줘요.
② JS에서 forEach(this.stamp)처럼 메서드를 떼어 넘기면 this가 사라져서 TypeError가 나요.
화살표 함수 () => this.stamp()로 감싸요.
③ C++ std::map의 emplace·insert는 이름이 이미 있으면 아무것도 바꾸지 않아서 new로 다시 만든 카드가 옛 카드로 남아요.
insert_or_assign이나 cards[name] = ...이 바꿔요(다만 []는 기본 생성자가 필요해요).
④ C++ StampCard card = cards.at(name);은 복사본이라 고친 도장이 사라져요.
StampCard& card로 받아요.
C#도 class 대신 struct로 만들면 값 형식이라 사전에서 꺼낼 때마다 복사본이 나와요.
⑤ 쿠폰이 0장일 때 쓰지 않게 막는 일은 use 메서드 안에 한 번만 적어요.
그래야 어디서 불러도 -1장이 되지 않아요.

Pythonmain.py · 46 줄
import sys  class StampCard:    def __init__(self, owner, goal):        self.owner = owner        self.goal = goal        self.stamps = 0        self.coupons = 0     def stamp(self):        self.stamps += 1        if self.stamps == self.goal:  # 목표에 닿으면 쿠폰 한 장으로 바꾼다            self.stamps = 0            self.coupons += 1     def buy(self, k):        for _ in range(k):            self.stamp()     def use(self):        if self.coupons == 0:            return False        self.coupons -= 1        return True     def describe(self):        return f"{self.owner} {self.stamps} {self.coupons}"  lines = sys.stdin.read().split("\n")q = int(lines[0])cards = {}out = []for line in lines[1 : q + 1]:    p = line.split()    if p[0] == "new":        cards[p[1]] = StampCard(p[1], int(p[2]))  # 이미 있으면 새 카드로 바뀐다    else:        card = cards[p[1]]        if p[0] == "buy":            card.buy(int(p[2]))        else:            card.use()        out.append(card.describe())print("\n".join(out))
JavaScriptmain.js · 48 줄
class StampCard {  constructor(owner, goal) {    this.owner = owner;    this.goal = goal;    this.stamps = 0;    this.coupons = 0;  }   stamp() {    this.stamps++;    if (this.stamps === this.goal) {      // 목표에 닿으면 쿠폰 한 장으로 바꾼다      this.stamps = 0;      this.coupons++;    }  }   buy(k) {    for (let i = 0; i < k; i++) this.stamp();  }   use() {    if (this.coupons === 0) return false;    this.coupons--;    return true;  }   describe() {    return `${this.owner} ${this.stamps} ${this.coupons}`;  }} const lines = require("fs").readFileSync(0, "utf8").split("\n");const q = Number(lines[0]);const cards = new Map();const out = [];for (let i = 1; i <= q; i++) {  const [cmd, name, num] = lines[i].trim().split(" ");  if (cmd === "new") {    cards.set(name, new StampCard(name, Number(num))); // 이미 있으면 새 카드로 바뀐다    continue;  }  const card = cards.get(name);  if (cmd === "buy") card.buy(Number(num));  else card.use();  out.push(card.describe());}console.log(out.join("\n"));
TypeScriptmain.ts · 49 줄
import { readFileSync } from "node:fs"; class StampCard {  private stamps = 0;  private coupons = 0;   // 매개변수 앞의 readonly가 같은 이름의 필드를 만들고 값을 넣어 준다  constructor(readonly owner: string, readonly goal: number) {}   private stamp(): void {    this.stamps++;    if (this.stamps === this.goal) {      // 목표에 닿으면 쿠폰 한 장으로 바꾼다      this.stamps = 0;      this.coupons++;    }  }   buy(k: number): void {    for (let i = 0; i < k; i++) this.stamp();  }   use(): boolean {    if (this.coupons === 0) return false;    this.coupons--;    return true;  }   describe(): string {    return `${this.owner} ${this.stamps} ${this.coupons}`;  }} const lines: string[] = readFileSync(0, "utf8").split("\n");const q: number = Number(lines[0]);const cards = new Map<string, StampCard>();const out: string[] = [];for (let i = 1; i <= q; i++) {  const [cmd, name, num] = lines[i].trim().split(" ");  if (cmd === "new") {    cards.set(name, new StampCard(name, Number(num))); // 이미 있으면 새 카드로 바뀐다    continue;  }  const card = cards.get(name)!;  if (cmd === "buy") card.buy(Number(num));  else card.use();  out.push(card.describe());}console.log(out.join("\n"));
JavaMain.java · 59 줄
import java.io.BufferedReader;import java.io.InputStreamReader;import java.util.HashMap;import java.util.Map; class StampCard {    private final String owner;    private final int goal;    private int stamps = 0;    private int coupons = 0;     StampCard(String owner, int goal) {        this.owner = owner; // this. 가 없으면 매개변수에 자기 자신을 넣을 뿐이다        this.goal = goal;    }     private void stamp() {        stamps++;        if (stamps == goal) { // 목표에 닿으면 쿠폰 한 장으로 바꾼다            stamps = 0;            coupons++;        }    }     void buy(int k) {        for (int i = 0; i < k; i++) stamp();    }     boolean use() {        if (coupons == 0) return false;        coupons--;        return true;    }     String describe() {        return owner + " " + stamps + " " + coupons;    }} public class Main {    public static void main(String[] args) throws Exception {        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));        int q = Integer.parseInt(in.readLine().trim());        Map<String, StampCard> cards = new HashMap<>();        StringBuilder out = new StringBuilder();        for (int i = 0; i < q; i++) {            String[] p = in.readLine().trim().split(" ");            if (p[0].equals("new")) {                cards.put(p[1], new StampCard(p[1], Integer.parseInt(p[2]))); // 이미 있으면 새 카드로 바뀐다                continue;            }            StampCard card = cards.get(p[1]);            if (p[0].equals("buy")) card.buy(Integer.parseInt(p[2]));            else card.use();            out.append(card.describe()).append('\n');        }        System.out.print(out);    }}
C#Main.cs · 65 줄
using System;using System.Collections.Generic;using System.Text; class StampCard // struct로 만들면 사전에서 꺼낼 때마다 복사본이 나온다{    public string Owner { get; }    public int Goal { get; }    public int Stamps { get; private set; }    public int Coupons { get; private set; }     public StampCard(string owner, int goal)    {        Owner = owner;        Goal = goal;    }     private void Stamp()    {        Stamps++;        if (Stamps == Goal) // 목표에 닿으면 쿠폰 한 장으로 바꾼다        {            Stamps = 0;            Coupons++;        }    }     public void Buy(int k)    {        for (int i = 0; i < k; i++) Stamp();    }     public bool Use()    {        if (Coupons == 0) return false;        Coupons--;        return true;    }     public override string ToString() => $"{Owner} {Stamps} {Coupons}";} class Program{    static void Main()    {        int q = int.Parse(Console.ReadLine()!.Trim());        var cards = new Dictionary<string, StampCard>();        var sb = new StringBuilder();        for (int i = 0; i < q; i++)        {            string[] p = Console.ReadLine()!.Trim().Split(' ');            if (p[0] == "new")            {                cards[p[1]] = new StampCard(p[1], int.Parse(p[2])); // 이미 있으면 새 카드로 바뀐다                continue;            }            StampCard card = cards[p[1]];            if (p[0] == "buy") card.Buy(int.Parse(p[2]));            else card.Use();            sb.Append(card).Append('\n');        }        Console.Write(sb);    }}
C++main.cpp · 62 줄
#include <iostream>#include <map>#include <string> class StampCard {public:    StampCard(std::string owner, int goal) : owner_(std::move(owner)), goal_(goal) {}     void buy(int k) {        for (int i = 0; i < k; i++) stamp();    }     bool use() {        if (coupons_ == 0) return false;        coupons_--;        return true;    }     std::string describe() const {        return owner_ + " " + std::to_string(stamps_) + " " + std::to_string(coupons_);    } private:    void stamp() {        stamps_++;        if (stamps_ == goal_) { // 목표에 닿으면 쿠폰 한 장으로 바꾼다            stamps_ = 0;            coupons_++;        }    }     std::string owner_;    int goal_;    int stamps_ = 0;    int coupons_ = 0;}; int main() {    std::ios::sync_with_stdio(false);    int q;    std::cin >> q;    std::map<std::string, StampCard> cards;    for (int i = 0; i < q; i++) {        std::string cmd, name;        std::cin >> cmd >> name;        if (cmd == "new") {            int g;            std::cin >> g;            cards.insert_or_assign(name, StampCard(name, g)); // emplace·insert는 이미 있으면 바꾸지 않는다            continue;        }        StampCard& card = cards.at(name); // &가 없으면 복사본을 고친다        if (cmd == "buy") {            int k;            std::cin >> k;            card.buy(k);        } else {            card.use();        }        std::cout << card.describe() << '\n';    }}