你尝试过这个看起来很可怕的谜题吗?
答案为 b 的第一道题是哪一道? (a) 2; (b) 3; (c) 4; (d) 5; (e) 6;
答案与其下一题答案相同的唯一一道题是(例如选项 e 表示第 6 题和第 7 题的答案相同): (a) 2; (b) 3; (c) 4; (d) 5; (e) 6;
在 5 个选项中,哪一道题的答案与本题(即第 3 题)相同? (a) 1; (b) 2; (c) 4; (d) 7; (e) 6;
答案为 a 的题目有多少道? (a) 0; (b) 1; (c) 2; (d) 3; (e) 4
下列哪一道题的答案与本题相同? (a) 10; (b) 9; (c) 8; (d) 7; (e) 6;
答案为 a 的题目数量,等于答案为以下哪项的题目数量: (a) b; (b) c; (c) d; (d) e; (e) 以上皆非
本题答案与下一题答案的差值是多少(例如 a 和 b 的差值为 1)? (a) 4; (b) 3; (c) 2; (d) 1; (e) 0;
答案为元音字母的题目有多少道?(只有 a 和 e 是元音。其他为辅音) (a) 2; (b) 3; (c) 4; (d) 5; (e) 6
答案为辅音字母的题目数量是: (a) 一个质数;(b) 一个阶乘数;(c) 一个平方数;(d) 一个立方数;(e) 5 的倍数
本题的答案是: (a) a; (b) b; (c) c; (d) d; (e) e;
说明: 1. 确保你的答案没有自相矛盾。例如,第一题的答案不可能是 b。 2. 确保对于每一道题,只有你的答案是正确的,所有其他选项必须是不正确的。例如,如果你第 5 题的答案是 a,那么第 9、8、7、6 题的答案都不可能是 a! 3. 确保你的答案不会使任何题目无效。例如,如果第 2 题和第 3 题的答案相同,且第 8 题和第 9 题的答案也相同,那么第 2 题将无效(因为没有题目是“唯一满足条件的题目”)。
用手解出这个谜题是可能的,但作为一名程序员,用程序来解决它更有趣!
如何用程序解决谜题
一种方法是:枚举所有可能的答案($5^{10} = 9765625$),对于每一道题,检查是否只有你的答案是正确的。伪代码如下:
forall(answer_list): bad = False for testing_question in [1,2,3,4,5,6,7,8,9,10]: for testing_option in ["a","b","c","d","e"]: # your answer should be correct if testing_option == answer_list[testing_question] and check(testing_question, testing_option) == False: bad = True # other options must be incorrect if testing_option != answer_list[testing_question] and check(testing_question, testing_option) == True: bad = True if not bad: print answer_list
这里 "answer_list" 是一个字母列表(下标从 1 开始),其中第 i 个字母是第 i 题的答案。
信不信由你,唯一的答案是:cdebeedcba(如果你喜欢在每个答案前加上题号,它是 1c2d3e4b5e6e7d8c9b10a)。
很神奇吧?还有更多。你希望你的程序能解决其他一些谜题,但首先,你需要用一种形式化语言来描述这个谜题。
形式化谜题
本题使用一种 LISP 方言来表示谜题。别担心如果你不懂 LISP,它有非常简单的语法。(f a b) 表示调用函数 f,参数为 a 和 b。这就像 C/C++/Java 中的 f(a, b)。类似地,(f a (g b c) d) 就像 C/C++/Java 中的 f(a, g(b, c), d)。
以下是如何描述谜题中一道题的示例:
- (equal (answer 3) (answer (option-value))) a. 1 b. 2 c. 4 d. 7 e. 6
其中涉及两个非常重要的内置函数:
(answer idx) 返回上述伪代码中的 answer_list[idx]。 (option-value) 返回 testing_option 的文本的“求值结果”,将其视为一个表达式。
在上面的例子中,如果 testing_option 是 "c",那么 (option-value) 返回 4(一个整数),因为 4 是该题选项 "c" 中给出的表达式。注意,testing_option 的文本可以是一个复杂的表达式,而不仅仅是一个简单的值。请参考样例输入。
上述函数 check(testing_question, testing_option) 可以实现如下:
check(testing_question, testing_option): 1. 设置函数 (option-value),使其返回 testing_question 的 testing_option 的求值结果 2. 对 testing_question 的 LISP 表达式进行求值(例如上面例子中的 (equal (answer 3) (answer (option-value)))) 3. 如果在求值过程中抛出了未处理的异常,则返回 False 4. 如果第 2 步的结果是布尔值,则返回它;否则返回 False
有一个特殊的选项表达式:"none-of-above"。"none-of-above" 的结果取决于其他选项的求值结果。在本题中,每道题最多只能有一个 "none-of-above",且它必须是最后一个选项。
实现细节
以下是本题所用 LISP 方言的详细信息:
- 有四种数据类型:整数、字符串、布尔值和函数。
- 只有两个布尔值:true 和 false。注意没有“布尔字面量”,所以你不需要关心是使用 #t 和 #f(像 Scheme 中那样),还是 t 和 nil(像 Common Lisp 中那样)来表示布尔常量。
- 整数始终是非负整数。
- 字符串字面量始终用双引号括起来,例如 "a string"。
- 没有变量。所有所谓的“标识符”(由字母和连字符组成)都是预定义的函数。
以下是预定义函数列表。以 ! 开头的函数意味着它可能会抛出异常,以 @ 开头的函数意味着它可以处理异常。像 C++/Java/Python 一样,一旦抛出异常,求值过程就会停止,除非有函数处理该异常。在下文中,iff 表示“当且仅当”。
基本函数
| 函数 | 说明 |
|---|---|
| (equal a b) | 当且仅当 a 和 b 类型相同且相等时返回 true。在本题中,你永远不需要比较两个函数。 |
| (option-value) | 如上所述。 |
| !(answer idx) | 如上所述。如果 idx 不是整数或不在 1~n 范围内(n 为题目总数),则抛出异常。 |
| !(answer-value idx) | 返回第 idx 题的选项 answer_list[idx] 的“求值结果”。出错时也会抛出异常。 |
谓词
谓词是一种特殊的函数。它总是接受任何类型的值并返回一个布尔值。
| 函数 | 说明 |
|---|---|
| prime-p | 当且仅当参数为正质数时返回 true |
| factorial-p | 不言自明 |
| square-p | 不言自明 |
| cubic-p | 不言自明 |
| vowel-p | 当且仅当参数为单个字母且为元音时返回 true |
| consonant-p | 不言自明 |
查询与统计
| 函数 | 说明 |
|---|---|
| !@(first-question pred) | 返回满足 pred 的第一道题的 id。如果未找到,则抛出异常。 |
| !@(last-question pred) | 返回满足 pred 的最后一道题的 id。如果未找到,则抛出异常。 |
| !@(only-question pred) | 返回满足 pred 的唯一一道题的 id。如果未找到或找到多于一道题,则抛出异常。 |
| @(count-question pred) | 返回满足 pred 的题目数量。 |
| !(diff-answer idx1 idx2) | 第 idx1 题和第 idx2 题答案的差值。出错时抛出异常,否则返回值始终为 0~m-1,其中 m 是每道题的选项数量。 |
注意,在前四个函数(带有 ‘@’ 标志的函数)中,如果在求值谓词时抛出了异常,该异常会被处理,且该谓词被视为不满足。例如,如果 answer_list 是 "abc",(count-question (make-answer-diff-next-equal 0)) 返回 0 且不会抛出异常,即使对第 3 题求值谓词(即 ((make-answer-diff-next-equal 0) 3))时抛出了异常。 然而,所有其他函数都不会处理异常。例如,如果只有 3 道题,(factorial-p (answer-value 5)) 将抛出异常而不是返回 false。
谓词生成器
还有一些函数可以即时创建谓词:
| 函数 | 说明 |
|---|---|
| !(make-answer-diff-next-equal num) | 返回一个谓词 (p idx),它对 (diff-answer idx idx+1) 求值,如果结果等于 num 则返回 true。如果 num 不是整数,则抛出异常。 |
| (make-answer-equal a) | 返回一个谓词 (p idx),它对 (answer idx) 求值,如果结果等于 a 则返回 true。 |
| (make-answer-is pred) | 返回一个谓词 (p idx),它对 (answer idx) 求值,如果结果满足 pred 则返回 true。 |
| (make-answer-value-equal a) | 不言自明。谓词对 (answer-value idx) 求值。 |
| (make-answer-value-is pred) | 不言自明。谓词对 (answer-value idx) 求值。 |
| !(make-is-multiple num) | 返回一个谓词 (p i),当且仅当 i 是整数且是 num 的倍数时返回 true。如果 num 不是整数,则抛出异常。 |
| !(make-equal val) | 返回一个谓词 (p v),当且仅当 (equal v val) 为 true 时返回 true。如果 val 既不是整数也不是字符串,则抛出异常。 |
| (make-not pred) | 返回一个谓词 (p v),当且仅当 (pred v) 为 false 时返回 true。 |
| (make-and pred1 pred2) | 返回一个谓词 (p v),当且仅当 (pred1 v) 和 (pred2 v) 均为 true 时返回 true。pred1 和 pred2 都需要被求值。不应进行短路操作。 |
| (make-or pred1 pred2) | 返回一个谓词 (p v),当且仅当 (pred1 v) 或 (pred2 v) 至少有一个为 true 时返回 true。pred1 和 pred2 都需要被求值。不应进行短路操作。 |
例如,(make-is-multiple 3) 返回一个“是 3 的倍数”的谓词,因此 ((make-is-multiple 3) 6) 返回 true,而 ((make-is-multiple 3) 10) 返回 false。类似地,(make-not (make-or square-p prime-p)) 返回一个“既不是平方数也不是质数”的谓词。
输入格式
最多有 50 个测试用例。每个测试用例以两个整数 n 和 m ($2 \le n \le 6, 2 \le m \le 5$) 开头,分别表示题目数量和每道题的选项数量。每道题用 m+1 行描述:题目的表达式和各个选项。题目编号为 1~n,选项标记为 a~e。选项是有效的表达式,且不会调用 option-value(调用 option-value 会使其递归!)。每道题后跟一个空行。大多数测试用例都很简单。
输出格式
对于每个测试用例,在第一行打印用例编号,然后按行打印答案列表,按升序排列。始终至少有一个答案。
样例
输入 1
3 3 (equal (option-value) (count-question (make-answer-equal "a"))) 3 0 1 (equal (option-value) "a") "c" "b" "a" ((option-value) (count-question (make-answer-equal "c"))) (make-and (make-is-multiple 2) (make-or factorial-p prime-p)) (make-not prime-p) "none-of-above" 3 2 (equal (option-value) (answer 2)) "a" "none-of-above" (equal (option-value) (first-question (make-answer-diff-next-equal 0))) 1 2 ((option-value) (last-question (make-answer-equal "b"))) (make-is-multiple 2) (make-not (make-is-multiple 2)) 3 2 (equal (option-value) (answer 1)) "a" "b" ((option-value) (last-question (make-answer-diff-next-equal 0))) (make-equal 2) "none-of-above" ((option-value) (only-question (make-answer-equal "b"))) (make-is-multiple 2) "none-of-above" 2 5 ((option-value) (diff-answer 1 2)) factorial-p prime-p (make-not square-p) (make-not cubic-p) "none-of-above" (equal (only-question (option-value)) 1) (make-answer-is consonant-p) (make-answer-is vowel-p) (make-answer-value-equal 1) (make-answer-value-is square-p) "none-of-above" 2 2 (option-value) (equal (first-question (make-answer-diff-next-equal 2)) (first-question (make-answer-diff-next-equal 2))) "none-of-above" (equal (option-value) 1) 1 2
输出 1
Case 1: bcb cca Case 2: aab Case 3: aba Case 4: ab ee Case 5: ba