{ }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()・標準入力に渡されます(実行中に入力を求めません)。

$ 準備完了

コードを入力して解くにはパソコンで開いてください。モバイルでは問題と解答を見られます。

ほかの言語では?言語別の解答を見る · 6 言語の解答

この問題はクラスのある Python・JavaScript・TypeScript・Java・C#・C++ だけを受け付けます(C にはクラスがなく、Craykon はまだクラスを作れません)。
コンストラクタは 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';    }}