{ }codelper
スタックP0348

給食室のトレーの山

給食室にトレーを積んでおく山が一つあり、最初は空である。
トレーには番号が書いてあり、同じ番号のトレーが複数あることもある。
命令が q 個、順に与えられる。
put x は番号 x のトレーを一枚、山のいちばん上に載せる。
take c は生徒 c 人が順番に、山のいちばん上のトレーを一枚ずつ取っていく。
山のトレーが c 枚より少なければ、あるだけ取り、残りの生徒はトレーなしで帰る。
take 命令ごとに、取ったトレーの番号を取った順に空白一つで区切って一行に出力せよ。
一枚も取れなかったときは、その行に - を出力せよ。
すべての命令を処理したあと、最後の行に山に残ったトレーの番号をいちばん上から下へ順に空白一つで区切って出力せよ。
残ったトレーがなければ、最後の行に - を出力せよ。

入力
一行目に q(1 ≤ q ≤ 9,000)、続く q 行に命令が一行に一つずつ与えられる。
1 ≤ x ≤ 1,000,000、1 ≤ c ≤ 9,000(すべて整数で、カンマなしの数字だけで与えられる)。
命令の単語と数は空白一つで区切る
出力
take 命令ごとに一行、そして最後の行に残ったトレー(番号はカンマなしの数字だけ)
例 1 · stdin
8
put 3
put 7
put 5
take 2
take 3
take 1
put 4
put 9
stdout
5 7
3
-
9 4
main.py
入力(stdin)

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

$ 準備完了

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

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

いちばん上に載せ、いちばん上から取り出すスタックは、言語ごとに使うものが違います。
Python はリストの append・pop()、JS・TS は配列の push・pop() を使い、末尾がいちばん上になります。
Java は ArrayDeque の push・pop、C# は Stack<int> の Push・Pop を使います。
C++ の std::stack は中身をたどれないので、最後の行を書くには vector の push_back・pop_back を使います。
C は配列と、いちばん上の位置を表す数一つで作ります。
Craykon はリストに 追加 で載せ、位置で取り除く(個数 - 1) で取り出します。
罠その一:空のスタックから取り出したときに起こることが違います。
Python の pop() は IndexError で、Java の ArrayDeque.pop() は NoSuchElementException で、C# の Pop() は InvalidOperationException で止まります。
例の take 3 はトレーが一枚しかないときに来るので、空かどうかを確かめずに三回取り出すとそこで止まります。
C++ の空の vector への pop_back と、C でいちばん上の位置が 0 より小さくなることは、何が起こるか決まっていません。
罠その二:JS の pop() は空の配列でもエラーなしに undefined を返します。
それまで入れてしまうと、何も取れていないのにリストが空でなくなり、例の take 1 が - の代わりに空行を出力します(join は undefined を空文字列として書きます)。
TS では pop() の型が number | undefined なので、number[] にそのまま入れると型エラーで教えてくれます。
罠その三:Python で pile[-c:] と一度に切り出すと便利ですが、切り出した部分は下側から並びます。
例の一行目が 5 7 ではなく 7 5 になります。
罠その四:スタックを最初から最後までたどるときの順番も違います。
Java の ArrayDeque と C# の Stack<int> はいちばん上からたどりますが、Java の古い Stack クラスはいちばん下からたどるので、例の最後の行が 4 9 になります。
Python のリスト、JS の配列、C++ の vector、Craykon のリストもいちばん下からなので、逆にたどるか反転させます(reversed(pile)、pile.reverse()、rbegin()、逆に())。

Pythonmain.py · 18 行
import sys lines = sys.stdin.read().split("\n")q = int(lines[0])pile = []  # 리스트의 끝이 더미의 맨 위out = []for i in range(1, q + 1):    cmd, v = lines[i].split()    v = int(v)    if cmd == "put":        pile.append(v)    else:        taken = []        while len(taken) < v and pile:  # 비었는지 먼저 묻고 꺼낸다            taken.append(pile.pop())        out.append(" ".join(map(str, taken)) if taken else "-")out.append(" ".join(map(str, reversed(pile))) if pile else "-")  # 맨 위부터print("\n".join(out))
JavaScriptmain.js · 18 行
const lines = require("fs").readFileSync(0, "utf8").split("\n");const q = Number(lines[0]);const pile = []; // 배열의 끝이 더미의 맨 위const out = [];for (let i = 1; i <= q; i++) {  const [cmd, t] = lines[i].split(" ");  const v = Number(t);  if (cmd === "put") {    pile.push(v);  } else {    const taken = [];    // 비었는지 먼저 묻고 꺼낸다(빈 배열의 pop()은 오류 없이 undefined를 준다)    while (taken.length < v && pile.length > 0) taken.push(pile.pop());    out.push(taken.length > 0 ? taken.join(" ") : "-");  }}out.push(pile.length > 0 ? pile.reverse().join(" ") : "-"); // 맨 위부터console.log(out.join("\n"));
TypeScriptmain.ts · 24 行
import { readFileSync } from "node:fs"; const lines: string[] = readFileSync(0, "utf8").split("\n");const q: number = Number(lines[0]);const pile: number[] = []; // 배열의 끝이 더미의 맨 위const out: string[] = [];for (let i = 1; i <= q; i++) {  const [cmd, t] = lines[i].split(" ");  const v: number = Number(t);  if (cmd === "put") {    pile.push(v);  } else {    const taken: number[] = [];    // pop()의 타입은 number | undefined: 비었는지 먼저 묻고 꺼낸다    while (taken.length < v) {      const top: number | undefined = pile.pop();      if (top === undefined) break;      taken.push(top);    }    out.push(taken.length > 0 ? taken.join(" ") : "-");  }}out.push(pile.length > 0 ? pile.reverse().join(" ") : "-"); // 맨 위부터console.log(out.join("\n"));
JavaMain.java · 41 行
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.util.ArrayDeque;import java.util.Deque; public class Main {    public static void main(String[] args) throws IOException {        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));        int q = Integer.parseInt(br.readLine().trim());        Deque<Integer> pile = new ArrayDeque<>(); // push·pop이 맨 위에서 일어난다        StringBuilder sb = new StringBuilder();        for (int i = 0; i < q; i++) {            String[] parts = br.readLine().split(" ");            int v = Integer.parseInt(parts[1]);            if (parts[0].equals("put")) {                pile.push(v);            } else {                int count = 0;                while (count < v && !pile.isEmpty()) { // 비었는지 먼저 묻고 꺼낸다                    if (count > 0) sb.append(' ');                    sb.append(pile.pop());                    count++;                }                if (count == 0) sb.append('-');                sb.append('\n');            }        }        if (pile.isEmpty()) {            sb.append('-');        } else {            boolean first = true;            for (int x : pile) { // ArrayDeque는 맨 위부터 돈다                if (!first) sb.append(' ');                sb.append(x);                first = false;            }        }        System.out.println(sb);    }}
C#Main.cs · 37 行
using System;using System.Collections.Generic;using System.Text; class Program{    static void Main()    {        int q = int.Parse(Console.ReadLine()!);        var pile = new Stack<int>();        var sb = new StringBuilder();        for (int i = 0; i < q; i++)        {            string[] parts = Console.ReadLine()!.Split(' ');            int v = int.Parse(parts[1]);            if (parts[0] == "put")            {                pile.Push(v);            }            else            {                int count = 0;                while (count < v && pile.Count > 0) // 비었는지 먼저 묻고 꺼낸다                {                    if (count > 0) sb.Append(' ');                    sb.Append(pile.Pop());                    count++;                }                if (count == 0) sb.Append('-');                sb.Append('\n');            }        }        // Stack<T>는 맨 위부터 돈다        sb.Append(pile.Count > 0 ? string.Join(" ", pile) : "-");        Console.WriteLine(sb);    }}
C++main.cpp · 37 行
#include <iostream>#include <string>#include <vector>using namespace std; int main() {    ios::sync_with_stdio(false);    cin.tie(nullptr);    int q;    cin >> q;    vector<int> pile; // std::stack은 속을 돌 수 없어서 vector를 스택으로 쓴다(끝이 맨 위)    for (int i = 0; i < q; i++) {        string cmd;        int v;        cin >> cmd >> v;        if (cmd == "put") {            pile.push_back(v);        } else {            int count = 0;            while (count < v && !pile.empty()) { // 비었는지 먼저 묻고 꺼낸다                if (count > 0) cout << ' ';                cout << pile.back();                pile.pop_back();                count++;            }            if (count == 0) cout << '-';            cout << '\n';        }    }    if (pile.empty()) cout << '-';    for (auto it = pile.rbegin(); it != pile.rend(); ++it) { // 맨 위부터        if (it != pile.rbegin()) cout << ' ';        cout << *it;    }    cout << '\n';    return 0;}
Cmain.c · 33 行
#include <stdio.h>#include <string.h> int pile[9000]; /* pile[top - 1]이 더미의 맨 위 */ int main(void) {    int q, top = 0;    if (scanf("%d", &q) != 1) return 0;    for (int i = 0; i < q; i++) {        char cmd[8];        int v;        if (scanf("%7s %d", cmd, &v) != 2) return 0;        if (strcmp(cmd, "put") == 0) {            pile[top++] = v;        } else {            int count = 0;            while (count < v && top > 0) { /* 비었는지 먼저 묻고 꺼낸다 */                if (count > 0) printf(" ");                printf("%d", pile[--top]);                count++;            }            if (count == 0) printf("-");            printf("\n");        }    }    if (top == 0) printf("-");    for (int i = top - 1; i >= 0; i--) { /* 맨 위부터 */        printf("%d", pile[i]);        if (i > 0) printf(" ");    }    printf("\n");    return 0;}
Craykonmain.cray · 37 行
q = コンソール.行読み().数に();pile = []; // 목록의 끝이 더미의 맨 위繰り返す (i = 0; i < q; i++){    parts = コンソール.行読み().分ける();    v = parts[1].数に();    もし (parts[0] == "put")    {        pile.追加(v);    }    でなければ    {        taken = [];        間 (taken.個数 < v && pile.個数 > 0)        {            taken.追加(pile[pile.個数 - 1]);            pile.位置で取り除く(pile.個数 - 1);        }        もし (taken.個数 == 0)        {            コンソール.書く("-");        }        でなければ        {            コンソール.書く(taken.つなぐ(" "));        }    }}もし (pile.個数 == 0){    コンソール.書く("-");}でなければ{    pile.逆に(); // 맨 위부터    コンソール.書く(pile.つなぐ(" "));}