{ }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# 中若用 struct 代替 class,它是值类型,每次从字典取出都是副本。
⑤ “没有券就不能用”的检查只在 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';    }}