{ }codelper
真偽値論理演算子P0324

修学旅行の申込書チェック

修学旅行の申込書 n 枚を検査する。
申込書 1 枚は、生徒の名前と、yes または no で答えた八つの項目からなる。
八つの項目は順に、保護者の同意、水泳の申し込み、健康確認書の提出、登山の申し込み、工作の申し込み、料理の申し込み、弁当の持参、給食の申し込みである。
申込書は次の四つの規則をすべて守らなければならない。
規則 1:保護者の同意が yes であること。
規則 2:水泳を申し込んだなら健康確認書を提出すること(水泳を申し込んでいなければ、確認書は出しても出さなくてもよい)。
規則 3:登山、工作、料理のうちちょうど一つだけを申し込むこと。
規則 4:弁当の持参と給食の申し込みのうちちょうど一つだけが yes であること。
申込書ごとに、与えられた順に 1 行ずつ出力する。
四つの規則をすべて守っていれば、名前のあとに空白を 1 つ空けて ok を出力する。
そうでなければ、名前のあとに破った規則の番号をすべて、小さい番号から空白 1 つずつで区切って出力する。
最後の行には、四つの規則をすべて守った申込書の数を出力する。

入力
1 行目に申込書の数 n が与えられる。
続く n 行に名前と八つの項目の答えが空白 1 つずつで区切って与えられる。
名前は英小文字 1〜10 文字で(yes、no、ok のこともあり、同じ名前が複数あることもある)、答えは yes または no である。
1 ≤ n ≤ 1,000
出力
n 行に申込書ごとに 名前 ok または 名前 破った規則の番号 を出力し、最後の行に合格した申込書の数を出力する
例 1 · stdin
4
mina yes yes yes no yes no yes no
jun no yes no yes yes yes yes yes
sora yes no no no no no no yes
dan yes no yes yes no no no yes
stdout
mina ok
jun 1 2 3 4
sora 3
dan ok
2
main.py
入力(stdin)

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

$ 準備完了

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

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

yes・no は文字列なので、まず真偽値に直して変数に入れ、規則を &&・||・!(Python は and・or・not)で書きます。
直す式は Python・C#・C++・Craykon が tok == "yes"、JS・TS が tok === "yes"、Java が tok.equals("yes")、C が strcmp(tok, "yes") == 0 です。
型の名前は Python・C#・C++ が bool、JS・TS・Java が boolean、C は <stdbool.h> を入れると bool が使え、Craykon は 真・偽 です。
落とし穴は六つ:① 文字列を直さずにそのまま条件に使うこと。
Python・JavaScript・TypeScript・Craykon では空文字列だけが偽なので、"no" も真として通り、エラーも出ません。
Java・C#・C++(std::string)はコンパイルエラーで止めてくれます。
② Java の tok == "yes" は中身ではなく同じオブジェクトかどうかをたずねるので、入力から読んだ yes にはいつも偽です。
③ C の strcmp は等しいとき 0 を返します。
strcmp(tok, "yes") をそのまま真偽値として使うと yes と no が逆になります。
④「水泳なら確認書」は swim && health ではありません。
そう書くと、水泳を申し込んでいない sora が規則 2 に引っかかります。
守っているのは !swim || health、破っているのは swim && !health です。
⑤「三つのうちちょうど一つ」を hike ^ craft ^ cook と書くと、三つとも yes の jun も真になります(真が奇数個なら真になるからです)。
真の個数を数えます。
Python・JavaScript・C・C++ は真が 1、偽が 0 として足せるので hike + craft + cook == 1 と書けます。
TypeScript・Java・C#・Craykon は真偽値どうしの足し算を許さないので(コンパイルエラー、Craykon は実行時エラー)、もし (a) { count += 1; } のように自分で数えるか、C# の Count(f => f) を使います。
⑥「二つのうちちょうど一つ」は packed || meal ではありません。
それでは両方 yes の jun が規則 4 を通ってしまいます。
二つの真偽値が異なるかを packed != meal でたずねれば済みます(JS・TS は !==)。

Pythonmain.py · 32 行
import sys  def main():    lines = sys.stdin.read().split("\n")    n = int(lines[0])    out = []    passed = 0    for line in lines[1:n + 1]:        parts = line.split()        name = parts[0]        # "no"도 빈 문자열이 아니라서 참으로 통한다 → 꼭 "yes"와 견줘 참·거짓으로 바꾼다        consent, swim, health, hike, craft, cook, packed, meal = (tok == "yes" for tok in parts[1:9])        broken = []        if not consent:            broken.append(1)        if swim and not health:  # '수영이면 확인서'가 깨지는 경우는 이것 하나뿐이다            broken.append(2)        if hike + craft + cook != 1:  # 참은 1, 거짓은 0으로 더해진다            broken.append(3)        if packed == meal:  # 둘 중 정확히 하나 = 두 값이 서로 다르다            broken.append(4)        if broken:            out.append(name + " " + " ".join(map(str, broken)))        else:            out.append(name + " ok")            passed += 1    out.append(str(passed))    print("\n".join(out))  main()
JavaScriptmain.js · 23 行
const lines = require("fs").readFileSync(0, "utf8").split("\n");const n = Number(lines[0]);const out = [];let passed = 0;for (let i = 1; i <= n; i++) {  const parts = lines[i].trim().split(" ");  const name = parts[0];  // "no"도 빈 문자열이 아니라서 참으로 통한다 → 꼭 "yes"와 견줘 참·거짓으로 바꾼다  const [consent, swim, health, hike, craft, cook, packed, meal] = parts.slice(1, 9).map((tok) => tok === "yes");  const broken = [];  if (!consent) broken.push(1);  if (swim && !health) broken.push(2); // '수영이면 확인서'가 깨지는 경우는 이것 하나뿐이다  if (hike + craft + cook !== 1) broken.push(3); // 더하면 true는 1, false는 0이 된다  if (packed === meal) broken.push(4); // 둘 중 정확히 하나 = 두 값이 서로 다르다  if (broken.length > 0) {    out.push(name + " " + broken.join(" "));  } else {    out.push(name + " ok");    passed++;  }}out.push(passed);console.log(out.join("\n"));
TypeScriptmain.ts · 32 行
import { readFileSync } from "node:fs"; // TypeScript는 boolean끼리 더하지 못하게 막는다 → 참인 것을 직접 센다function countTrue(...flags: boolean[]): number {  return flags.filter((f) => f).length;} const lines: string[] = readFileSync(0, "utf8").split("\n");const n: number = Number(lines[0]);const out: string[] = [];let passed = 0;for (let i = 1; i <= n; i++) {  const parts: string[] = lines[i].trim().split(" ");  const name: string = parts[0];  // "no"도 빈 문자열이 아니라서 참으로 통한다 → 꼭 "yes"와 견줘 boolean으로 바꾼다  const [consent, swim, health, hike, craft, cook, packed, meal]: boolean[] = parts    .slice(1, 9)    .map((tok) => tok === "yes");  const broken: number[] = [];  if (!consent) broken.push(1);  if (swim && !health) broken.push(2); // '수영이면 확인서'가 깨지는 경우는 이것 하나뿐이다  if (countTrue(hike, craft, cook) !== 1) broken.push(3);  if (packed === meal) broken.push(4); // 둘 중 정확히 하나 = 두 값이 서로 다르다  if (broken.length > 0) {    out.push(name + " " + broken.join(" "));  } else {    out.push(name + " ok");    passed++;  }}out.push(String(passed));console.log(out.join("\n"));
JavaMain.java · 44 行
import java.io.*;import java.util.*; public class Main {    // Java의 boolean은 수가 아니라서 더할 수 없다 → 1이나 0으로 바꿔 센다    static int countTrue(boolean... flags) {        int count = 0;        for (boolean f : flags) {            if (f) count++;        }        return count;    }     public static void main(String[] args) throws IOException {        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));        int n = Integer.parseInt(br.readLine().trim());        StringBuilder sb = new StringBuilder();        int passed = 0;        for (int i = 0; i < n; i++) {            StringTokenizer st = new StringTokenizer(br.readLine());            String name = st.nextToken();            boolean[] v = new boolean[8];            for (int j = 0; j < 8; j++) {                v[j] = st.nextToken().equals("yes"); // ==는 같은 객체인지를 묻는다. 내용은 equals로 견준다            }            boolean consent = v[0], swim = v[1], health = v[2];            boolean hike = v[3], craft = v[4], cook = v[5];            boolean packed = v[6], meal = v[7];            StringBuilder broken = new StringBuilder();            if (!consent) broken.append(" 1");            if (swim && !health) broken.append(" 2"); // '수영이면 확인서'가 깨지는 경우는 이것 하나뿐이다            if (countTrue(hike, craft, cook) != 1) broken.append(" 3");            if (packed == meal) broken.append(" 4"); // 둘 중 정확히 하나 = 두 값이 서로 다르다            if (broken.length() > 0) {                sb.append(name).append(broken).append('\n');            } else {                sb.append(name).append(" ok\n");                passed++;            }        }        sb.append(passed).append('\n');        System.out.print(sb);    }}
C#Main.cs · 41 行
using System;using System.Collections.Generic;using System.Linq;using System.Text; class Program{    static void Main()    {        int n = int.Parse(Console.ReadLine()!.Trim());        var sb = new StringBuilder();        int passed = 0;        for (int i = 0; i < n; i++)        {            string[] parts = Console.ReadLine()!.Split(' ', StringSplitOptions.RemoveEmptyEntries);            string name = parts[0];            // C#의 ==는 문자열의 내용을 견준다            bool[] v = parts.Skip(1).Take(8).Select(tok => tok == "yes").ToArray();            bool consent = v[0], swim = v[1], health = v[2];            bool hike = v[3], craft = v[4], cook = v[5];            bool packed = v[6], meal = v[7];            var broken = new List<int>();            if (!consent) broken.Add(1);            if (swim && !health) broken.Add(2); // '수영이면 확인서'가 깨지는 경우는 이것 하나뿐이다            // bool은 수가 아니라서 더할 수 없다 → 참인 것을 센다            if (new[] { hike, craft, cook }.Count(f => f) != 1) broken.Add(3);            if (packed == meal) broken.Add(4); // 둘 중 정확히 하나 = 두 값이 서로 다르다            if (broken.Count > 0)            {                sb.Append(name).Append(' ').Append(string.Join(" ", broken)).Append('\n');            }            else            {                sb.Append(name).Append(" ok\n");                passed++;            }        }        sb.Append(passed).Append('\n');        Console.Write(sb);    }}
C++main.cpp · 38 行
#include <iostream>#include <string> int main() {    std::ios::sync_with_stdio(false);    int n;    std::cin >> n;    int passed = 0;    for (int i = 0; i < n; i++) {        std::string name;        std::cin >> name;        bool v[8];        for (int j = 0; j < 8; j++) {            std::string tok;            std::cin >> tok;            v[j] = tok == "yes"; // std::string의 ==는 내용을 견준다        }        bool consent = v[0], swim = v[1], health = v[2];        bool hike = v[3], craft = v[4], cook = v[5];        bool packed = v[6], meal = v[7];        bool r1 = consent;        bool r2 = !swim || health; // 수영을 하지 않으면 확인서는 묻지 않는다        bool r3 = hike + craft + cook == 1; // bool은 1 아니면 0이라 더해서 셀 수 있다        bool r4 = packed != meal;           // 둘 중 정확히 하나 = 두 값이 서로 다르다        if (r1 && r2 && r3 && r4) {            std::cout << name << " ok\n";            passed++;        } else {            std::cout << name;            if (!r1) std::cout << " 1";            if (!r2) std::cout << " 2";            if (!r3) std::cout << " 3";            if (!r4) std::cout << " 4";            std::cout << '\n';        }    }    std::cout << passed << '\n';}
Cmain.c · 39 行
#include <stdbool.h>#include <stdio.h>#include <string.h> int main(void) {    int n;    if (scanf("%d", &n) != 1) return 1;    int passed = 0;    for (int i = 0; i < n; i++) {        char name[11];        bool v[8];        if (scanf("%10s", name) != 1) return 1;        for (int j = 0; j < 8; j++) {            char tok[4];            if (scanf("%3s", tok) != 1) return 1;            v[j] = strcmp(tok, "yes") == 0; // strcmp는 같을 때 0을 돌려준다        }        bool consent = v[0], swim = v[1], health = v[2];        bool hike = v[3], craft = v[4], cook = v[5];        bool packed = v[6], meal = v[7];        bool r1 = consent;        bool r2 = !swim || health; // 수영을 하지 않으면 확인서는 묻지 않는다        bool r3 = hike + craft + cook == 1; // bool은 1 아니면 0이라 더해서 셀 수 있다        bool r4 = packed != meal;           // 둘 중 정확히 하나 = 두 값이 서로 다르다        if (r1 && r2 && r3 && r4) {            printf("%s ok\n", name);            passed++;        } else {            printf("%s", name);            if (!r1) printf(" 1");            if (!r2) printf(" 2");            if (!r3) printf(" 3");            if (!r4) printf(" 4");            printf("\n");        }    }    printf("%d\n", passed);    return 0;}
Craykonmain.cray · 62 行
// 참·거짓은 수가 아니라서 더할 수 없다 → 참인 것을 직접 센다참개수(a, b, c){    count = 0;    もし (a)    {        count += 1;    }    もし (b)    {        count += 1;    }    もし (c)    {        count += 1;    }    返す count;} n = コンソール.行読み().数に();passed = 0;繰り返す (i = 0; i < n; i++){    parts = コンソール.行読み().分ける();    name = parts[0];    // 문자열을 "yes"와 견줘 참·거짓으로 바꾼다    consent = parts[1] == "yes";    swim = parts[2] == "yes";    health = parts[3] == "yes";    hike = parts[4] == "yes";    craft = parts[5] == "yes";    cook = parts[6] == "yes";    packed = parts[7] == "yes";    meal = parts[8] == "yes";    broken = "";    もし (!consent)    {        broken += " 1";    }    もし (swim && !health)    {        broken += " 2"; // '수영이면 확인서'가 깨지는 경우는 이것 하나뿐이다    }    もし (참개수(hike, craft, cook) != 1)    {        broken += " 3";    }    もし (packed == meal)    {        broken += " 4"; // 둘 중 정확히 하나 = 두 값이 서로 다르다    }    もし (broken == "")    {        コンソール.書く(name + " ok");        passed += 1;    }    でなければ    {        コンソール.書く(name + broken);    }}コンソール.書く(passed);