{ }codelper
布尔值逻辑运算符P0324

检查修学旅行报名表

检查 n 张修学旅行报名表。
一张报名表由学生姓名和用 yes 或 no 回答的八个项目组成。
八个项目依次是:监护人同意、报名游泳、提交健康确认书、报名登山、报名手工、报名烹饪、自带便当、报名学校供餐。
报名表必须遵守下面全部四条规则。
规则 1:监护人同意必须是 yes。
规则 2:如果报名了游泳,就必须提交健康确认书(没有报名游泳时,确认书交不交都可以)。
规则 3:登山、手工、烹饪中必须恰好只报名一项。
规则 4:自带便当和学校供餐中必须恰好只有一项是 yes。
按给出的顺序,对每张报名表输出一行。
如果四条规则全部遵守,输出姓名、一个空格和 ok。
否则在姓名后面输出所有违反的规则编号,从小到大,各用一个空格隔开。
最后一行输出四条规则全部遵守的报名表数量。

输入
第一行给出报名表数 n。
接下来 n 行,每行给出姓名和八个项目的回答,用一个空格隔开。
姓名是 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);