{ }codelper
メソッドクラスP0323

いっしょに流れる砂時計

砂時計が n 個あり、i 番の砂時計には砂が c_i 粒入っている。
はじめはどの砂時計も砂がすべて下の部屋にある。
時間が流れると、上の部屋に砂がある砂時計はそれぞれ 1 秒に 1 粒ずつ上の部屋から下の部屋へ砂が落ち、上の部屋が空になるとその砂時計はそれ以上流れない。
砂時計をクラスとして作り、「ひっくり返す」「t 秒流す」「上の部屋が空かどうかをたずねる」「上の部屋に残っている砂の数をたずねる」をメソッドとして作って、次の q 個の命令を順に処理せよ。
flip i:i 番の砂時計をひっくり返す(上の部屋と下の部屋の砂の数が入れ替わる)。
何も出力しない。
wait t:すべての砂時計に t 秒が流れる。
この t 秒の間に上の部屋が空になった砂時計の数を出力する。
命令の前から上の部屋が空だった砂時計は数えない。
until i:命令の直前に i 番の砂時計の上の部屋に残っていた砂の数と同じ秒数が、すべての砂時計に同じように流れる(i 番の上の部屋がすでに空なら時間は流れない)。
そのあと、最初から今までに wait と until で流れた時間をすべて足した秒数を出力する。
flipdone:上の部屋が空の砂時計をすべてひっくり返し、ひっくり返した砂時計の数を出力する。
命令をすべて処理したあと、1 番から n 番まで砂時計ごとに上の部屋の砂の数と下の部屋の砂の数を空白 1 つで区切って 1 行ずつ出力する。

入力
1 行目に砂時計の数 n と命令の数 q が空白 1 つで区切って与えられる。
2 行目に c_1, …, c_n が空白 1 つずつで区切って与えられる。
続く q 行に命令が 1 行に 1 つずつ与えられる。
1 ≤ n ≤ 100、1 ≤ q ≤ 1,000、1 ≤ c_i ≤ 1,000,000、1 ≤ t ≤ 1,000,000、1 ≤ i ≤ n
出力
wait、until、flipdone の命令ごとに答えを 1 行に 1 つずつ出力する(カンマなしの数字だけ)。
そのあと n 行に、砂時計ごとに上の部屋と下の部屋の砂の数を空白 1 つで区切って出力する
例 1 · stdin
2 7
4 7
flipdone
until 1
flipdone
until 2
wait 5
flipdone
flip 2
stdout
2
4
1
7
1
2
4 0
0 7
main.py
入力(stdin)

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

$ 準備完了

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

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

この問題はクラスのある Python・JavaScript・TypeScript・Java・C#・C++ だけを受け付けます(C にはクラスがなく、Craykon はまだクラスを作れません)。
メソッドはオブジェクトの中の値を読んだり変えたりする関数です。
Python は def run(self, seconds): のように最初の引数 self を自分で書き、JS・TS は run(seconds) { … } の中で this.top と書いて届きます。
Java・C#・C++ は top と書くだけでフィールドです。
C++ は値を変えないメソッドの後ろに const を付けます(bool isEmpty() const)。
ひっくり返すのは二つのフィールドの入れ替えで、Python は self.top, self.bottom = self.bottom, self.top、JS・TS は [this.top, this.bottom] = [this.bottom, this.top]、C# は (top, bottom) = (bottom, top)、C++ は std::swap(top_, bottom_) と 1 行で書き、Java は一時変数を一つ置きます。
落とし穴は六つ:① top = bottom; bottom = top; のように順に代入すると上の部屋の値が先に消え、二つの部屋が同じ数になります。
② メソッドを呼ぶときにかっこを忘れること。
Python の if g.is_empty: と JavaScript の if (g.isEmpty) はエラーなしに動きますが、メソッドそのものはいつも真なので、空でない砂時計までひっくり返します(例の二つ目の flipdone が 1 ではなく 2 になります)。
TypeScript は「この条件は常に真」というエラー(TS2774)で、Java・C#・C++ はコンパイルエラーで知らせてくれます。
かっこなしで書きたければ Python @property、JS・TS get isEmpty()、C# のプロパティ bool IsEmpty => top == 0; にします。
③ 流すときは min(seconds, top) だけ移します。
ただ top -= seconds と書くと上の部屋が負になります。
④「今回空になったか」と「今空であるか」は違います。
流すメソッドが top == 0 を返すと、前から空だった砂時計まで数えます(例の wait 5 が 1 ではなく 2 になります)。
⑤ until で流す時間はループの前に一度だけたずねて変数に入れます。
ループの中で g.run(target.remaining()) のようにたずね直すと、i 番が空になったあとは 0 になり、後ろの番号の砂時計は流れません。
⑥ C++ の for (Hourglass g : glasses) は砂時計をコピーするので、コピーに時間が流れます。
Hourglass& g と書けば本物が変わります。
Python・JS・TS・Java・C# のループ変数は同じオブジェクトを指すので、そのまま変わります。

Pythonmain.py · 65 行
import sys  class Hourglass:    def __init__(self, capacity):        # 처음에는 모래가 전부 아래 칸에 있다        self.top = 0        self.bottom = capacity     def flip(self):        self.top, self.bottom = self.bottom, self.top     def run(self, seconds):        """seconds초가 흐른다. 이 동안 위 칸이 비게 되었으면 True를 돌려준다."""        moved = min(seconds, self.top)        self.top -= moved        self.bottom += moved        return moved > 0 and self.top == 0     def is_empty(self):        return self.top == 0     def remaining(self):        return self.top  def main():    lines = sys.stdin.read().split("\n")    n, q = map(int, lines[0].split())    glasses = [Hourglass(int(c)) for c in lines[1].split()]    elapsed = 0    out = []    for line in lines[2:2 + q]:        parts = line.split()        cmd = parts[0]        if cmd == "flip":            glasses[int(parts[1]) - 1].flip()        elif cmd == "wait":            t = int(parts[1])            elapsed += t            done = 0            for g in glasses:                if g.run(t):                    done += 1            out.append(str(done))        elif cmd == "until":            # 흐를 시간은 반복 전에 한 번만 묻는다(반복 중에 물으면 i번이 비고 나서 0이 된다)            t = glasses[int(parts[1]) - 1].remaining()            elapsed += t            for g in glasses:                g.run(t)            out.append(str(elapsed))        else:  # flipdone            flipped = 0            for g in glasses:                if g.is_empty():  # 괄호를 빼면 메서드 자체라 늘 참이다                    g.flip()                    flipped += 1            out.append(str(flipped))    for g in glasses:        out.append(f"{g.top} {g.bottom}")    print("\n".join(out))  main()
JavaScriptmain.js · 64 行
class Hourglass {  constructor(capacity) {    // 처음에는 모래가 전부 아래 칸에 있다    this.top = 0;    this.bottom = capacity;  }   flip() {    [this.top, this.bottom] = [this.bottom, this.top];  }   // seconds초가 흐른다. 이 동안 위 칸이 비게 되었으면 true를 돌려준다  run(seconds) {    const moved = Math.min(seconds, this.top);    this.top -= moved;    this.bottom += moved;    return moved > 0 && this.top === 0;  }   isEmpty() {    return this.top === 0;  }   remaining() {    return this.top;  }} const lines = require("fs").readFileSync(0, "utf8").split("\n");const [n, q] = lines[0].trim().split(" ").map(Number);const glasses = lines[1].trim().split(" ").slice(0, n).map((c) => new Hourglass(Number(c)));let elapsed = 0;const out = [];for (let k = 2; k < 2 + q; k++) {  const [cmd, arg] = lines[k].trim().split(" ");  if (cmd === "flip") {    glasses[Number(arg) - 1].flip();  } else if (cmd === "wait") {    const t = Number(arg);    elapsed += t;    let done = 0;    for (const g of glasses) {      if (g.run(t)) done++;    }    out.push(done);  } else if (cmd === "until") {    // 흐를 시간은 반복 전에 한 번만 묻는다(반복 중에 물으면 i번이 비고 나서 0이 된다)    const t = glasses[Number(arg) - 1].remaining();    elapsed += t;    for (const g of glasses) g.run(t);    out.push(elapsed);  } else {    let flipped = 0;    for (const g of glasses) {      if (g.isEmpty()) { // 괄호를 빼면 함수 자체라 늘 참이다        g.flip();        flipped++;      }    }    out.push(flipped);  }}for (const g of glasses) out.push(`${g.top} ${g.bottom}`);console.log(out.join("\n"));
TypeScriptmain.ts · 68 行
import { readFileSync } from "node:fs"; class Hourglass {  private top = 0; // 처음에는 모래가 전부 아래 칸에 있다   constructor(private bottom: number) {}   flip(): void {    [this.top, this.bottom] = [this.bottom, this.top];  }   // seconds초가 흐른다. 이 동안 위 칸이 비게 되었으면 true를 돌려준다  run(seconds: number): boolean {    const moved = Math.min(seconds, this.top);    this.top -= moved;    this.bottom += moved;    return moved > 0 && this.top === 0;  }   isEmpty(): boolean {    return this.top === 0;  }   remaining(): number {    return this.top;  }   describe(): string {    return `${this.top} ${this.bottom}`;  }} const lines: string[] = readFileSync(0, "utf8").split("\n");const [n, q] = lines[0].trim().split(" ").map(Number);const glasses: Hourglass[] = lines[1].trim().split(" ").slice(0, n).map((c) => new Hourglass(Number(c)));let elapsed = 0;const out: string[] = [];for (let k = 2; k < 2 + q; k++) {  const [cmd, arg] = lines[k].trim().split(" ");  if (cmd === "flip") {    glasses[Number(arg) - 1].flip();  } else if (cmd === "wait") {    const t = Number(arg);    elapsed += t;    let done = 0;    for (const g of glasses) {      if (g.run(t)) done++;    }    out.push(String(done));  } else if (cmd === "until") {    // 흐를 시간은 반복 전에 한 번만 묻는다(반복 중에 물으면 i번이 비고 나서 0이 된다)    const t = glasses[Number(arg) - 1].remaining();    elapsed += t;    for (const g of glasses) g.run(t);    out.push(String(elapsed));  } else {    let flipped = 0;    for (const g of glasses) {      if (g.isEmpty()) { // 괄호를 빼면 TypeScript가 컴파일 오류로 알려 준다        g.flip();        flipped++;      }    }    out.push(String(flipped));  }}for (const g of glasses) out.push(g.describe());console.log(out.join("\n"));
JavaMain.java · 83 行
import java.io.*;import java.util.*; public class Main {    static class Hourglass {        private int top = 0; // 처음에는 모래가 전부 아래 칸에 있다        private int bottom;         Hourglass(int capacity) {            this.bottom = capacity;        }         void flip() {            int t = top;            top = bottom;            bottom = t;        }         // seconds초가 흐른다. 이 동안 위 칸이 비게 되었으면 true를 돌려준다        boolean run(int seconds) {            int moved = Math.min(seconds, top);            top -= moved;            bottom += moved;            return moved > 0 && top == 0;        }         boolean isEmpty() {            return top == 0;        }         int remaining() {            return top;        }         String describe() {            return top + " " + bottom;        }    }     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());        int q = Integer.parseInt(st.nextToken());        st = new StringTokenizer(br.readLine());        Hourglass[] glasses = new Hourglass[n];        for (int i = 0; i < n; i++) glasses[i] = new Hourglass(Integer.parseInt(st.nextToken()));        int elapsed = 0;        StringBuilder sb = new StringBuilder();        for (int k = 0; k < q; k++) {            st = new StringTokenizer(br.readLine());            String cmd = st.nextToken();            if (cmd.equals("flip")) {                glasses[Integer.parseInt(st.nextToken()) - 1].flip();            } else if (cmd.equals("wait")) {                int t = Integer.parseInt(st.nextToken());                elapsed += t;                int done = 0;                for (Hourglass g : glasses) {                    if (g.run(t)) done++;                }                sb.append(done).append('\n');            } else if (cmd.equals("until")) {                // 흐를 시간은 반복 전에 한 번만 묻는다(반복 중에 물으면 i번이 비고 나서 0이 된다)                int t = glasses[Integer.parseInt(st.nextToken()) - 1].remaining();                elapsed += t;                for (Hourglass g : glasses) g.run(t);                sb.append(elapsed).append('\n');            } else {                int flipped = 0;                for (Hourglass g : glasses) {                    if (g.isEmpty()) {                        g.flip();                        flipped++;                    }                }                sb.append(flipped).append('\n');            }        }        for (Hourglass g : glasses) sb.append(g.describe()).append('\n');        System.out.print(sb);    }}
C#Main.cs · 99 行
using System;using System.Linq;using System.Text; class Hourglass{    private int top = 0; // 처음에는 모래가 전부 아래 칸에 있다    private int bottom;     public Hourglass(int capacity)    {        bottom = capacity;    }     public void Flip()    {        (top, bottom) = (bottom, top);    }     // seconds초가 흐른다. 이 동안 위 칸이 비게 되었으면 true를 돌려준다    public bool Run(int seconds)    {        int moved = Math.Min(seconds, top);        top -= moved;        bottom += moved;        return moved > 0 && top == 0;    }     public bool IsEmpty()    {        return top == 0;    }     public int Remaining()    {        return top;    }     public string Describe()    {        return $"{top} {bottom}";    }} class Program{    static void Main()    {        int[] head = Console.ReadLine()!.Split(' ', StringSplitOptions.RemoveEmptyEntries).Select(int.Parse).ToArray();        int q = head[1];        Hourglass[] glasses = Console.ReadLine()!.Split(' ', StringSplitOptions.RemoveEmptyEntries)            .Select(c => new Hourglass(int.Parse(c))).ToArray();        int elapsed = 0;        var sb = new StringBuilder();        for (int k = 0; k < q; k++)        {            string[] parts = Console.ReadLine()!.Split(' ', StringSplitOptions.RemoveEmptyEntries);            string cmd = parts[0];            if (cmd == "flip")            {                glasses[int.Parse(parts[1]) - 1].Flip();            }            else if (cmd == "wait")            {                int t = int.Parse(parts[1]);                elapsed += t;                int done = 0;                foreach (Hourglass g in glasses)                {                    if (g.Run(t)) done++;                }                sb.Append(done).Append('\n');            }            else if (cmd == "until")            {                // 흐를 시간은 반복 전에 한 번만 묻는다(반복 중에 물으면 i번이 비고 나서 0이 된다)                int t = glasses[int.Parse(parts[1]) - 1].Remaining();                elapsed += t;                foreach (Hourglass g in glasses) g.Run(t);                sb.Append(elapsed).Append('\n');            }            else            {                int flipped = 0;                foreach (Hourglass g in glasses)                {                    if (g.IsEmpty())                    {                        g.Flip();                        flipped++;                    }                }                sb.Append(flipped).Append('\n');            }        }        foreach (Hourglass g in glasses) sb.Append(g.Describe()).Append('\n');        Console.Write(sb);    }}
C++main.cpp · 78 行
#include <algorithm>#include <iostream>#include <string>#include <utility>#include <vector> class Hourglass {public:    explicit Hourglass(int capacity) : bottom_(capacity) {}     void flip() { std::swap(top_, bottom_); }     // seconds초가 흐른다. 이 동안 위 칸이 비게 되었으면 true를 돌려준다    bool run(int seconds) {        int moved = std::min(seconds, top_);        top_ -= moved;        bottom_ += moved;        return moved > 0 && top_ == 0;    }     // 값을 바꾸지 않는 메서드에는 const를 붙인다    bool isEmpty() const { return top_ == 0; }    int remaining() const { return top_; }    int bottom() const { return bottom_; } private:    int top_ = 0; // 처음에는 모래가 전부 아래 칸에 있다    int bottom_;}; int main() {    std::ios::sync_with_stdio(false);    int n, q;    std::cin >> n >> q;    std::vector<Hourglass> glasses;    for (int i = 0; i < n; i++) {        int c;        std::cin >> c;        glasses.emplace_back(c);    }    int elapsed = 0;    for (int k = 0; k < q; k++) {        std::string cmd;        std::cin >> cmd;        if (cmd == "flip") {            int i;            std::cin >> i;            glasses[i - 1].flip();        } else if (cmd == "wait") {            int t;            std::cin >> t;            elapsed += t;            int done = 0;            for (Hourglass& g : glasses) { // &가 없으면 복사본에 시간이 흐른다                if (g.run(t)) done++;            }            std::cout << done << '\n';        } else if (cmd == "until") {            int i;            std::cin >> i;            // 흐를 시간은 반복 전에 한 번만 묻는다(반복 중에 물으면 i번이 비고 나서 0이 된다)            int t = glasses[i - 1].remaining();            elapsed += t;            for (Hourglass& g : glasses) g.run(t);            std::cout << elapsed << '\n';        } else {            int flipped = 0;            for (Hourglass& g : glasses) {                if (g.isEmpty()) {                    g.flip();                    flipped++;                }            }            std::cout << flipped << '\n';        }    }    for (const Hourglass& g : glasses) std::cout << g.remaining() << ' ' << g.bottom() << '\n';}