{ }codelper
クラスメソッドP0079

共有する番号札

窓口が m 個ある店を窓口クラスで作れ。
番号札の通し番号はすべての窓口が共有する一つの値なのでクラスに一つだけ置き(クラス変数・静的フィールド)、窓口ごとの受け取った番号札のリストは各窓口オブジェクトが別々に持つ。
窓口は 1 から m まで番号のついた別々のオブジェクト m 個である。
一行目に m と命令の数 q、続く q 行に命令が一つずつ与えられる。"take w":通し番号を 1 増やし(最初の発行は 1)、その番号札を窓口 w のリストの末尾に入れ、"通し番号 リストの長さ" を出力する(リストの長さは入れたあとの窓口 w のリストにある番号札の数)。"reset w":窓口 w のリストだけを空にする。
ほかの窓口と通し番号はそのまま。
出力はない。"show w":窓口 w のリストにある番号札の数を先に書き、続けてリストの通し番号を入れた順に空白一つで区切って一行に出力する(空なら 0 だけを出力)。

入力
1 ≤ m ≤ 100、1 ≤ q ≤ 5000、1 ≤ w ≤ m
出力
take と show の命令ごとに一行
例 1 · stdin
3 9
take 1
take 2
take 1
show 1
reset 1
show 1
take 1
show 1
show 3
stdout
1 1
2 1
3 2
2 1 3
0
4 1
1 4
0
main.py
入力(stdin)

実行すると、これらの行が順に input()・標準入力に渡されます(実行中に入力を求めません)。

$ 準備完了

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

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

この問題はクラスのある Python・JavaScript・TypeScript・Java・C#・C++ だけを受け付けます(C にはクラスがなく、Craykon はまだクラスを作れません)。
要は「すべてのオブジェクトが共有する値」と「オブジェクトごとに別の値」を分けることです。
共有する通し番号は Java・C# なら static int、JS・TS なら static lastSerial = 0、C++ なら inline static int(C++17。
inline がないとクラスの外に int Window::lastSerial = 0; という定義が別に必要)、Python ならクラス本体の last_serial = 0 です。
落とし穴は三つあります。
Python の self.last_serial += 1 はクラス変数を変えず、そのオブジェクトだけに同じ名前の新しい値を作るので、窓口ごとに番号が別々に数えられます。
Window.last_serial += 1 のようにクラス名で変えましょう。
逆に served = [] をクラス本体に置くと、一つのリストをすべての窓口が共有します。
オブジェクトごとのリストは __init__ の中で self.served = [] として作ります。
JS では静的フィールドを this.lastSerial で読むとオブジェクトにないので undefined になり NaN が出ます(TS はコンパイルエラーで防いでくれます)。
最後に、オブジェクトを m 個作るとき Python [Window()] * m、JS Array(m).fill(new Window()) は一つのオブジェクトを m 個のマスが共有して指します。
Python [Window() for _ in range(m)]、JS Array.from({ length: m }, () => new Window()) のようにマスごとに新しく作りましょう。
C++ std::vector<Window> windows(m) は値として m 個を別々に作るので大丈夫です。

Pythonmain.py · 35 行
import sys  class Window:    last_serial = 0  # 클래스 변수: 모든 창구가 하나를 함께 쓴다     def __init__(self):        self.served = []  # 객체마다 따로 갖는 목록(여기서 만들어야 한다)     def take(self):        Window.last_serial += 1  # self.last_serial += 1 이면 이 창구만의 값이 새로 생긴다        self.served.append(Window.last_serial)        return Window.last_serial, len(self.served)     def reset(self):        self.served = []     def show(self):        return " ".join(map(str, [len(self.served)] + self.served))  data = sys.stdin.read().split()m, q = int(data[0]), int(data[1])windows = [Window() for _ in range(m)]out = []for i in range(q):    cmd, w = data[2 + 2 * i], windows[int(data[3 + 2 * i]) - 1]    if cmd == "take":        serial, local = w.take()        out.append(f"{serial} {local}")    elif cmd == "reset":        w.reset()    else:        out.append(w.show())print("\n".join(out))
JavaScriptmain.js · 35 行
class Window {  static lastSerial = 0; // 정적 필드: 모든 창구가 하나를 함께 쓴다   constructor() {    this.served = []; // 객체마다 따로 갖는 목록  }   take() {    Window.lastSerial++; // this.lastSerial 은 객체에 없어서 undefined    this.served.push(Window.lastSerial);    return `${Window.lastSerial} ${this.served.length}`;  }   reset() {    this.served = [];  }   show() {    return [this.served.length, ...this.served].join(" ");  }} const data = require("fs").readFileSync(0, "utf8").trim().split(/\s+/);const m = Number(data[0]);const q = Number(data[1]);const windows = Array.from({ length: m }, () => new Window());const out = [];for (let i = 0; i < q; i++) {  const cmd = data[2 + 2 * i];  const w = windows[Number(data[3 + 2 * i]) - 1];  if (cmd === "take") out.push(w.take());  else if (cmd === "reset") w.reset();  else out.push(w.show());}console.log(out.join("\n"));
TypeScriptmain.ts · 34 行
import { readFileSync } from "node:fs"; class Window {  private static lastSerial = 0; // 정적 필드: 모든 창구가 하나를 함께 쓴다  private served: number[] = []; // 객체마다 따로 갖는 목록   take(): string {    Window.lastSerial++;    this.served.push(Window.lastSerial);    return `${Window.lastSerial} ${this.served.length}`;  }   reset(): void {    this.served = [];  }   show(): string {    return [this.served.length, ...this.served].join(" ");  }} const data: string[] = readFileSync(0, "utf8").trim().split(/\s+/);const m: number = Number(data[0]);const q: number = Number(data[1]);const windows: Window[] = Array.from({ length: m }, () => new Window());const out: string[] = [];for (let i = 0; i < q; i++) {  const cmd = data[2 + 2 * i];  const w = windows[Number(data[3 + 2 * i]) - 1];  if (cmd === "take") out.push(w.take());  else if (cmd === "reset") w.reset();  else out.push(w.show());}console.log(out.join("\n"));
JavaMain.java · 49 行
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.util.ArrayList;import java.util.List;import java.util.StringTokenizer; class Window {    private static int lastSerial = 0; // static: 모든 창구가 하나를 함께 쓴다    private List<Integer> served = new ArrayList<>(); // 객체마다 따로     String take() {        lastSerial++;        served.add(lastSerial);        return lastSerial + " " + served.size();    }     void reset() { served = new ArrayList<>(); }     String show() {        StringBuilder sb = new StringBuilder().append(served.size());        for (int s : served) sb.append(' ').append(s);        return sb.toString();    }} public class Main {    public static void main(String[] args) throws IOException {        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));        StringTokenizer st = new StringTokenizer(in.readLine());        int m = Integer.parseInt(st.nextToken());        int q = Integer.parseInt(st.nextToken());        Window[] windows = new Window[m];        for (int i = 0; i < m; i++) windows[i] = new Window();         StringBuilder out = new StringBuilder();        for (int i = 0; i < q; i++) {            st = new StringTokenizer(in.readLine());            String cmd = st.nextToken();            Window w = windows[Integer.parseInt(st.nextToken()) - 1];            switch (cmd) {                case "take" -> out.append(w.take()).append('\n');                case "reset" -> w.reset();                default -> out.append(w.show()).append('\n');            }        }        System.out.print(out);    }}
C#Main.cs · 45 行
using System;using System.Collections.Generic;using System.Text; class Window{    private static int lastSerial = 0; // static: 모든 창구가 하나를 함께 쓴다    private List<int> served = new List<int>(); // 객체마다 따로     public string Take()    {        lastSerial++;        served.Add(lastSerial);        return $"{lastSerial} {served.Count}";    }     public void Reset() => served = new List<int>();     public string Show() => served.Count == 0 ? "0" : $"{served.Count} {string.Join(" ", served)}";} class Program{    static void Main()    {        string[] first = Console.ReadLine().Split(' ');        int m = int.Parse(first[0]), q = int.Parse(first[1]);        var windows = new Window[m];        for (int i = 0; i < m; i++) windows[i] = new Window();         var sb = new StringBuilder();        for (int i = 0; i < q; i++)        {            string[] p = Console.ReadLine().Trim().Split(' ');            Window w = windows[int.Parse(p[1]) - 1];            switch (p[0])            {                case "take": sb.Append(w.Take()).Append('\n'); break;                case "reset": w.Reset(); break;                default: sb.Append(w.Show()).Append('\n'); break;            }        }        Console.Write(sb);    }}
C++main.cpp · 40 行
#include <iostream>#include <string>#include <vector> class Window {public:    std::string take() {        lastSerial++;        served_.push_back(lastSerial);        return std::to_string(lastSerial) + " " + std::to_string(served_.size());    }     void reset() { served_.clear(); }     std::string show() const {        std::string s = std::to_string(served_.size());        for (int x : served_) s += " " + std::to_string(x);        return s;    } private:    inline static int lastSerial = 0; // static: 모든 창구가 하나를 함께 쓴다(inline 없으면 클래스 밖에 정의가 필요)    std::vector<int> served_;          // 객체마다 따로}; int main() {    std::ios::sync_with_stdio(false);    int m, q;    std::cin >> m >> q;    std::vector<Window> windows(m); // 서로 다른 Window 객체 m개    for (int i = 0; i < q; i++) {        std::string cmd;        int w;        std::cin >> cmd >> w;        Window& win = windows[w - 1];        if (cmd == "take") std::cout << win.take() << '\n';        else if (cmd == "reset") win.reset();        else std::cout << win.show() << '\n';    }}