{ }codelper
番兵ループ入れ子の条件P0139

ATM の状態を追う

カード一枚で使う ATM をまねる。
一行目に口座の残高 B、四桁の暗証番号 P、機械に入っている現金 M が空白一つずつを挟んで与えられる。
P は 0 で始まることがある(例:0123)。
二行目から命令が一行に一つずつ与えられ、quit 命令が出たら終わる。
quit のあとに行があっても処理しない。
機械の状態は idle(カードなし)、card(カードは入ったが暗証番号をまだ当てていない)、ready(暗証番号を当てた)のいずれかで、最初は idle である。
機械は暗証番号を間違えた回数を覚えている。
この回数はカードを抜いて入れ直しても続き、暗証番号を当てると 0 になる。
命令ごとに次のように処理して一行を出力する。
insert:カードを取り込んだことがあれば "利用停止" である。
そうでなく状態が idle でなければ "無効な操作" である。
それ以外は状態を card にして "カード挿入" である。
pin x:状態が card でなければ "無効な操作" である(間違えた回数には入らない)。
x が P と文字どおり同じなら、状態を ready に、間違えた回数を 0 にして "確認" である。
違えば間違えた回数を 1 増やす。
その回数が 3 になると機械はカードを取り込み、状態を idle にして "カード回収" であり、そうでなければ "不一致" である。
withdraw n:状態が ready でなければ "無効な操作" である。
n が残高より大きければ "残高不足" である。
そうでなく n が機械の現金より大きければ "現金不足" である。
それ以外は残高と機械の現金から n ずつ引き、新しい残高を出力する。
deposit n:状態が ready でなければ "無効な操作" である。
それ以外は残高と機械の現金に n ずつ足し、新しい残高を出力する。
balance:状態が ready でなければ "無効な操作" で、そうでなければ残高を出力する。
eject:状態が idle なら "無効な操作" である。
それ以外は状態を idle にして "カード返却" である。
quit が出たら、最後の行に残高と機械の現金を空白一つを挟んで出力する。

入力
0 ≤ B ≤ 10^9、0 ≤ M ≤ 10^9、P は数字四つ、quit までの命令は 1〜1,000 行(quit を含む)で quit は必ず出る、pin の x は数字 1〜6 個、1 ≤ n ≤ 10^6
出力
quit の前の命令ごとに一行、最後に残高と現金を一行
例 1 · stdin
100000 4821 50000
insert
pin 4821
withdraw 30000
withdraw 80000
withdraw 30000
deposit 15000
withdraw 30000
balance
eject
withdraw 1000
quit
insert
stdout
カード挿入
確認
70000
残高不足
現金不足
85000
55000
55000
カード返却
無効な操作
55000 5000
例 2 · stdin
50000 0123 30000
balance
insert
pin 123
pin 1230
eject
insert
pin 0000
insert
quit
stdout
無効な操作
カード挿入
不一致
不一致
カード返却
カード挿入
カード回収
利用停止
50000 30000
main.py
入力(stdin)

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

$ 準備完了

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

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

quit という終わりの印が出るまで読むループです:一行読んで while (cmd != "quit") { 処理; 次の行を読む }。
入力の終わり(EOF)まで読んでしまうと、quit のあとの行(最初の例の最後の insert)まで処理して答えが変わります。
まず命令で分け、その中で状態と値をもう一度聞く入れ子の条件です。
内側の条件の順番が規則なので、withdraw で現金不足を残高不足より先に聞くと、両方足りないときに答えが変わります。
暗証番号は文字列で比べます。
数に変えると 0123 と 123 が同じになり、二つ目の例の pin 123 が正しいことになってしまいます。
文字列が等しいかは Python・C#・JS・TS・C++ ==(std::string)、Java は equals(== は同じオブジェクトかを比べる)、C は strcmp(a, b) == 0 です。
C の a == b は配列のアドレスを比べるので常に偽です。
状態の名前は C enum { IDLE, CARD, READY }、C++ enum class、ほかは文字列定数にできます。
Craykon は 間 (p[0] != "quit") で回り、分岐は もし … でなければ もし … でなければ です。
カードを入れるたびに間違えた回数を 0 に戻すのもよくある間違いです(二つ目の例では入れ直したあと一回間違えるだけでカードを取り込みます)。

Pythonmain.py · 69 行
import sys first = sys.stdin.readline().split()balance, pin, cash = int(first[0]), first[1], int(first[2])  # PIN은 0으로 시작할 수 있어 문자열로 둔다state = "idle"      # idle(카드 없음) → card(카드 들어옴) → ready(PIN 확인됨)wrong = 0           # 틀린 횟수: 카드를 뺐다 넣어도 이어진다retained = False    # 세 번 틀려 기계가 카드를 가져갔나out = [] line = sys.stdin.readline().split()while line[0] != "quit":  # quit이 끝 표시: 그 뒤의 줄은 읽지 않는다    cmd = line[0]    if cmd == "insert":        if retained:            out.append("利用停止")        elif state != "idle":            out.append("無効な操作")        else:            state = "card"            out.append("カード挿入")    elif cmd == "pin":        if state != "card":            out.append("無効な操作")        elif line[1] == pin:            state = "ready"            wrong = 0            out.append("確認")        else:            wrong += 1            if wrong == 3:                retained = True                state = "idle"                out.append("カード回収")            else:                out.append("不一致")    elif cmd == "withdraw":        n = int(line[1])        if state != "ready":            out.append("無効な操作")        elif n > balance:            out.append("残高不足")        elif n > cash:            out.append("現金不足")        else:            balance -= n            cash -= n            out.append(str(balance))    elif cmd == "deposit":        if state != "ready":            out.append("無効な操作")        else:            n = int(line[1])            balance += n            cash += n            out.append(str(balance))    elif cmd == "balance":        if state != "ready":            out.append("無効な操作")        else:            out.append(str(balance))    else:  # eject        if state == "idle":            out.append("無効な操作")        else:            state = "idle"            out.append("カード返却")    line = sys.stdin.readline().split()out.append(f"{balance} {cash}")print("\n".join(out))
JavaScriptmain.js · 69 行
const lines = require("fs").readFileSync(0, "utf8").split("\n");const first = lines[0].trim().split(/\s+/);let balance = Number(first[0]);const pin = first[1]; // PIN은 0으로 시작할 수 있어 문자열로 둔다let cash = Number(first[2]);let state = "idle"; // idle(카드 없음) → card(카드 들어옴) → ready(PIN 확인됨)let wrong = 0; // 틀린 횟수: 카드를 뺐다 넣어도 이어진다let retained = false; // 세 번 틀려 기계가 카드를 가져갔나const out = []; let i = 1;let p = lines[i].trim().split(/\s+/);while (p[0] !== "quit") { // quit이 끝 표시: 그 뒤의 줄은 읽지 않는다  const cmd = p[0];  if (cmd === "insert") {    if (retained) out.push("利用停止");    else if (state !== "idle") out.push("無効な操作");    else {      state = "card";      out.push("カード挿入");    }  } else if (cmd === "pin") {    if (state !== "card") out.push("無効な操作");    else if (p[1] === pin) {      state = "ready";      wrong = 0;      out.push("確認");    } else {      wrong++;      if (wrong === 3) {        retained = true;        state = "idle";        out.push("カード回収");      } else {        out.push("不一致");      }    }  } else if (cmd === "withdraw") {    const n = Number(p[1]);    if (state !== "ready") out.push("無効な操作");    else if (n > balance) out.push("残高不足");    else if (n > cash) out.push("現金不足");    else {      balance -= n;      cash -= n;      out.push(String(balance));    }  } else if (cmd === "deposit") {    if (state !== "ready") out.push("無効な操作");    else {      const n = Number(p[1]);      balance += n;      cash += n;      out.push(String(balance));    }  } else if (cmd === "balance") {    out.push(state !== "ready" ? "無効な操作" : String(balance));  } else { // eject    if (state === "idle") out.push("無効な操作");    else {      state = "idle";      out.push("カード返却");    }  }  i++;  p = lines[i].trim().split(/\s+/);}out.push(`${balance} ${cash}`);console.log(out.join("\n"));
TypeScriptmain.ts · 70 行
import { readFileSync } from "node:fs";const lines: string[] = readFileSync(0, "utf8").split("\n");const first: string[] = lines[0].trim().split(/\s+/);let balance: number = Number(first[0]);const pin: string = first[1]; // PIN은 0으로 시작할 수 있어 문자열로 둔다let cash: number = Number(first[2]);let state: string = "idle"; // idle(카드 없음) → card(카드 들어옴) → ready(PIN 확인됨)let wrong = 0; // 틀린 횟수: 카드를 뺐다 넣어도 이어진다let retained = false; // 세 번 틀려 기계가 카드를 가져갔나const out: string[] = []; let i = 1;let p: string[] = lines[i].trim().split(/\s+/);while (p[0] !== "quit") { // quit이 끝 표시: 그 뒤의 줄은 읽지 않는다  const cmd = p[0];  if (cmd === "insert") {    if (retained) out.push("利用停止");    else if (state !== "idle") out.push("無効な操作");    else {      state = "card";      out.push("カード挿入");    }  } else if (cmd === "pin") {    if (state !== "card") out.push("無効な操作");    else if (p[1] === pin) {      state = "ready";      wrong = 0;      out.push("確認");    } else {      wrong++;      if (wrong === 3) {        retained = true;        state = "idle";        out.push("カード回収");      } else {        out.push("不一致");      }    }  } else if (cmd === "withdraw") {    const n: number = Number(p[1]);    if (state !== "ready") out.push("無効な操作");    else if (n > balance) out.push("残高不足");    else if (n > cash) out.push("現金不足");    else {      balance -= n;      cash -= n;      out.push(String(balance));    }  } else if (cmd === "deposit") {    if (state !== "ready") out.push("無効な操作");    else {      const n: number = Number(p[1]);      balance += n;      cash += n;      out.push(String(balance));    }  } else if (cmd === "balance") {    out.push(state !== "ready" ? "無効な操作" : String(balance));  } else { // eject    if (state === "idle") out.push("無効な操作");    else {      state = "idle";      out.push("カード返却");    }  }  i++;  p = lines[i].trim().split(/\s+/);}out.push(`${balance} ${cash}`);console.log(out.join("\n"));
JavaMain.java · 76 行
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader; public class Main {    public static void main(String[] args) throws IOException {        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));        String[] first = br.readLine().trim().split("\\s+");        int balance = Integer.parseInt(first[0]);        String pin = first[1]; // PIN은 0으로 시작할 수 있어 문자열로 둔다        int cash = Integer.parseInt(first[2]);        String state = "idle"; // idle(카드 없음) → card(카드 들어옴) → ready(PIN 확인됨)        int wrong = 0; // 틀린 횟수: 카드를 뺐다 넣어도 이어진다        boolean retained = false; // 세 번 틀려 기계가 카드를 가져갔나        StringBuilder out = new StringBuilder();         String[] p = br.readLine().trim().split("\\s+");        while (!p[0].equals("quit")) { // quit이 끝 표시: 그 뒤의 줄은 읽지 않는다            String cmd = p[0];            if (cmd.equals("insert")) {                if (retained) out.append("利用停止\n");                else if (!state.equals("idle")) out.append("無効な操作\n");                else {                    state = "card";                    out.append("カード挿入\n");                }            } else if (cmd.equals("pin")) {                if (!state.equals("card")) out.append("無効な操作\n");                else if (p[1].equals(pin)) {                    state = "ready";                    wrong = 0;                    out.append("確認\n");                } else {                    wrong++;                    if (wrong == 3) {                        retained = true;                        state = "idle";                        out.append("カード回収\n");                    } else {                        out.append("不一致\n");                    }                }            } else if (cmd.equals("withdraw")) {                int n = Integer.parseInt(p[1]);                if (!state.equals("ready")) out.append("無効な操作\n");                else if (n > balance) out.append("残高不足\n");                else if (n > cash) out.append("現金不足\n");                else {                    balance -= n;                    cash -= n;                    out.append(balance).append('\n');                }            } else if (cmd.equals("deposit")) {                if (!state.equals("ready")) out.append("無効な操作\n");                else {                    int n = Integer.parseInt(p[1]);                    balance += n;                    cash += n;                    out.append(balance).append('\n');                }            } else if (cmd.equals("balance")) {                if (!state.equals("ready")) out.append("無効な操作\n");                else out.append(balance).append('\n');            } else { // eject                if (state.equals("idle")) out.append("無効な操作\n");                else {                    state = "idle";                    out.append("カード返却\n");                }            }            p = br.readLine().trim().split("\\s+");        }        out.append(balance).append(' ').append(cash).append('\n');        System.out.print(out);    }}
C#Main.cs · 98 行
using System;using System.Text; class Program{    static void Main()    {        string[] first = Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries);        int balance = int.Parse(first[0]);        string pin = first[1]; // PIN은 0으로 시작할 수 있어 문자열로 둔다        int cash = int.Parse(first[2]);        string state = "idle"; // idle(카드 없음) → card(카드 들어옴) → ready(PIN 확인됨)        int wrong = 0; // 틀린 횟수: 카드를 뺐다 넣어도 이어진다        bool retained = false; // 세 번 틀려 기계가 카드를 가져갔나        var sb = new StringBuilder();         string[] p = Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries);        while (p[0] != "quit") // quit이 끝 표시: 그 뒤의 줄은 읽지 않는다        {            string cmd = p[0];            if (cmd == "insert")            {                if (retained) sb.Append("利用停止\n");                else if (state != "idle") sb.Append("無効な操作\n");                else                {                    state = "card";                    sb.Append("カード挿入\n");                }            }            else if (cmd == "pin")            {                if (state != "card") sb.Append("無効な操作\n");                else if (p[1] == pin)                {                    state = "ready";                    wrong = 0;                    sb.Append("確認\n");                }                else                {                    wrong++;                    if (wrong == 3)                    {                        retained = true;                        state = "idle";                        sb.Append("カード回収\n");                    }                    else                    {                        sb.Append("不一致\n");                    }                }            }            else if (cmd == "withdraw")            {                int n = int.Parse(p[1]);                if (state != "ready") sb.Append("無効な操作\n");                else if (n > balance) sb.Append("残高不足\n");                else if (n > cash) sb.Append("現金不足\n");                else                {                    balance -= n;                    cash -= n;                    sb.Append(balance).Append('\n');                }            }            else if (cmd == "deposit")            {                if (state != "ready") sb.Append("無効な操作\n");                else                {                    int n = int.Parse(p[1]);                    balance += n;                    cash += n;                    sb.Append(balance).Append('\n');                }            }            else if (cmd == "balance")            {                if (state != "ready") sb.Append("無効な操作\n");                else sb.Append(balance).Append('\n');            }            else // eject            {                if (state == "idle") sb.Append("無効な操作\n");                else                {                    state = "idle";                    sb.Append("カード返却\n");                }            }            p = Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries);        }        sb.Append(balance).Append(' ').Append(cash).Append('\n');        Console.Write(sb);    }}
C++main.cpp · 78 行
#include <iostream>#include <sstream>#include <string> enum class State { Idle, Card, Ready }; // 카드 없음 → 카드 들어옴 → PIN 확인됨 int main() {    int balance, cash;    std::string pin; // PIN은 0으로 시작할 수 있어 문자열로 둔다    std::cin >> balance >> pin >> cash;    State state = State::Idle;    int wrong = 0;         // 틀린 횟수: 카드를 뺐다 넣어도 이어진다    bool retained = false; // 세 번 틀려 기계가 카드를 가져갔나    std::ostringstream out;     std::string cmd;    std::cin >> cmd;    while (cmd != "quit") { // quit이 끝 표시: 그 뒤의 줄은 읽지 않는다        if (cmd == "insert") {            if (retained) out << "利用停止\n";            else if (state != State::Idle) out << "無効な操作\n";            else {                state = State::Card;                out << "カード挿入\n";            }        } else if (cmd == "pin") {            std::string guess;            std::cin >> guess;            if (state != State::Card) out << "無効な操作\n";            else if (guess == pin) {                state = State::Ready;                wrong = 0;                out << "確認\n";            } else {                wrong++;                if (wrong == 3) {                    retained = true;                    state = State::Idle;                    out << "カード回収\n";                } else {                    out << "不一致\n";                }            }        } else if (cmd == "withdraw") {            int n;            std::cin >> n;            if (state != State::Ready) out << "無効な操作\n";            else if (n > balance) out << "残高不足\n";            else if (n > cash) out << "現金不足\n";            else {                balance -= n;                cash -= n;                out << balance << '\n';            }        } else if (cmd == "deposit") {            int n;            std::cin >> n;            if (state != State::Ready) out << "無効な操作\n";            else {                balance += n;                cash += n;                out << balance << '\n';            }        } else if (cmd == "balance") {            if (state != State::Ready) out << "無効な操作\n";            else out << balance << '\n';        } else { // eject            if (state == State::Idle) out << "無効な操作\n";            else {                state = State::Idle;                out << "カード返却\n";            }        }        std::cin >> cmd;    }    out << balance << ' ' << cash << '\n';    std::cout << out.str();}
Cmain.c · 76 行
#include <stdbool.h>#include <stdio.h>#include <stdlib.h>#include <string.h> enum { IDLE, CARD, READY }; /* 카드 없음 → 카드 들어옴 → PIN 확인됨 */ int main(void) {    int balance, cash;    char pin[8]; /* PIN은 0으로 시작할 수 있어 문자열로 둔다 */    char line[64], cmd[16], arg[16];    if (!fgets(line, sizeof line, stdin)) return 0;    sscanf(line, "%d %7s %d", &balance, pin, &cash);    int state = IDLE;    int wrong = 0;          /* 틀린 횟수: 카드를 뺐다 넣어도 이어진다 */    bool retained = false;  /* 세 번 틀려 기계가 카드를 가져갔나 */     while (fgets(line, sizeof line, stdin)) {        arg[0] = '\0';        sscanf(line, "%15s %15s", cmd, arg);        if (strcmp(cmd, "quit") == 0) break; /* quit이 끝 표시: 그 뒤의 줄은 읽지 않는다 */        if (strcmp(cmd, "insert") == 0) {            if (retained) puts("利用停止");            else if (state != IDLE) puts("無効な操作");            else {                state = CARD;                puts("カード挿入");            }        } else if (strcmp(cmd, "pin") == 0) {            if (state != CARD) puts("無効な操作");            else if (strcmp(arg, pin) == 0) {                state = READY;                wrong = 0;                puts("確認");            } else {                wrong++;                if (wrong == 3) {                    retained = true;                    state = IDLE;                    puts("カード回収");                } else {                    puts("不一致");                }            }        } else if (strcmp(cmd, "withdraw") == 0) {            int n = atoi(arg);            if (state != READY) puts("無効な操作");            else if (n > balance) puts("残高不足");            else if (n > cash) puts("現金不足");            else {                balance -= n;                cash -= n;                printf("%d\n", balance);            }        } else if (strcmp(cmd, "deposit") == 0) {            if (state != READY) puts("無効な操作");            else {                int n = atoi(arg);                balance += n;                cash += n;                printf("%d\n", balance);            }        } else if (strcmp(cmd, "balance") == 0) {            if (state != READY) puts("無効な操作");            else printf("%d\n", balance);        } else { /* eject */            if (state == IDLE) puts("無効な操作");            else {                state = IDLE;                puts("カード返却");            }        }    }    printf("%d %d\n", balance, cash);    return 0;}
Craykonmain.cray · 117 行
첫줄 = コンソール.行読み().空白除去().分ける();잔액 = 첫줄[0].数に();비밀번호 = 첫줄[1]; // PIN은 0으로 시작할 수 있어 문자열로 둔다현금 = 첫줄[2].数に();상태 = "idle"; // idle(카드 없음) → card(카드 들어옴) → ready(PIN 확인됨)틀린수 = 0; // 틀린 횟수: 카드를 뺐다 넣어도 이어진다가져감 = 偽; // 세 번 틀려 기계가 카드를 가져갔나 p = コンソール.行読み().空白除去().分ける();間 (p[0] != "quit"){    명령 = p[0];    もし (명령 == "insert")    {        もし (가져감)        {            コンソール.書く("利用停止");        }        でなければ もし (상태 != "idle")        {            コンソール.書く("無効な操作");        }        でなければ        {            상태 = "card";            コンソール.書く("カード挿入");        }    }    でなければ もし (명령 == "pin")    {        もし (상태 != "card")        {            コンソール.書く("無効な操作");        }        でなければ もし (p[1] == 비밀번호)        {            상태 = "ready";            틀린수 = 0;            コンソール.書く("確認");        }        でなければ        {            틀린수++;            もし (틀린수 == 3)            {                가져감 = 真;                상태 = "idle";                コンソール.書く("カード回収");            }            でなければ            {                コンソール.書く("不一致");            }        }    }    でなければ もし (명령 == "withdraw")    {        n = p[1].数に();        もし (상태 != "ready")        {            コンソール.書く("無効な操作");        }        でなければ もし (n > 잔액)        {            コンソール.書く("残高不足");        }        でなければ もし (n > 현금)        {            コンソール.書く("現金不足");        }        でなければ        {            잔액 -= n;            현금 -= n;            コンソール.書く(잔액);        }    }    でなければ もし (명령 == "deposit")    {        もし (상태 != "ready")        {            コンソール.書く("無効な操作");        }        でなければ        {            n = p[1].数に();            잔액 += n;            현금 += n;            コンソール.書く(잔액);        }    }    でなければ もし (명령 == "balance")    {        もし (상태 != "ready")        {            コンソール.書く("無効な操作");        }        でなければ        {            コンソール.書く(잔액);        }    }    でなければ    {        もし (상태 == "idle")        {            コンソール.書く("無効な操作");        }        でなければ        {            상태 = "idle";            コンソール.書く("カード返却");        }    }    p = コンソール.行読み().空白除去().分ける();}コンソール.書く(잔액 + " " + 현금);