githubEdit

ACWing 478侦探推理

题目描述

问题链接:478. 侦探推理 - AcWing题库arrow-up-right

明明同学最近迷上了侦探漫画《柯南》并沉醉于推理游戏之中,于是他召集了一群同学玩推理游戏。

游戏的内容是这样的,明明的同学们先商量好由其中的一个人充当罪犯(在明明不知情的情况下),明明的任务就是找出这个罪犯。

接着,明明逐个询问每一个同学,被询问者可能会说:

证词内容
证词含义

I am guilty.

我是罪犯

I am not guilty.

我不是罪犯

XXX is guilty.

XXX是罪犯(XXX表示某个同学的名字)

XXX is not guilty.

XXX不是罪犯

Today is XXX.

今天是XXX(XXX表示星期几,是Monday、Tuesday、Wednesday、Thursday、Friday、Saturday、Sunday中的一个)

证词中出现的其他话,都不列入逻辑推理的内容。

明明所知道的是,他的同学中有 NN 个人始终说假话,其余的人始终说真。

现在,明明需要你帮助他从他同学的话中推断出谁是真正的凶手,请记住,凶手只有一个!

输入格式

输入由若干行组成,第一行有三个整数,M、NM、N 和 PP;MM 是参加游戏的明明的同学数,NN 是其中始终说谎的人数,PP 是证言的总数。

接下来 MM 行,每行是明明的一个同学的名字(英文字母组成,没有空格,全部大写)。

往后有 PP 行,每行开始是某个同学的名字,紧跟着一个冒号和一个空格,后面是一句证词,符合前表中所列格式。

证词每行不会超过 250250 个字符。

输入中不会出现连续的两个空格,而且每行开头和结尾也没有空格。

输出格式

如果你的程序能确定谁是罪犯,则输出他的名字;如果程序判断出不止一个人可能是罪犯,则输出 Cannot Determine;如果程序判断出没有人可能成为罪犯,则输出 Impossible

数据范围

1≤M≤201≤M≤20, 1≤N≤M1≤N≤M, 1≤P≤1001≤P≤100

输入样例:

输出样例:

算法思路

起初我想的是枚举说谎的N个人,但是这样的话就会有$2^{n}$种情况,时间复杂度有点过于大了。后来想了想,干脆枚举这M个人谁是凶手就好了,然后循环一周里的每一天,因为只有一个凶手,所以每句证词都能够判断出是正确的还是错误的,理想情况下能直接求出说谎的人数,从而直接判断是否等于N即可。但是实际情况下可能会出现有的人不说话,甚至有的人既说真话又说假话,那么就要考虑到这些特殊情况了,如果既说真话又说假话,那么可以直接跳过这一次判断,进行下一次判断即可;如果有的人没说话或者说了废话,判断不了说的是真还是假,那么我们最终能够判断出说谎的人数、说真话的人数、判断不了的人数;这种情况下,如果说谎的人数小于等于N并且说谎的人数加上判断不了的人数大于等于N,那么这种情况也应该算作在内。如果最后发现可能有不止一个人是罪犯,那么就输出Cannot Determine;如果程序结束还没有人成为罪犯,输出Impossible。理论上这种情况下的时间复杂度是O(M * 7 * P)人,但是每句证词的发言人不能直接获得,需要遍历一遍存放名字的数组,这种情况下是$O(7M^{2}P)$。这里比较考验代码结构的设计,应当使用模块化设计方法,但是我这里从一开始想偷懒懒得写函数,最后代码的结构非常繁冗,建议大家写复杂代码的时候使用模块化设计,下面这个yxc写的题解就非常好:AcWing 478. 侦探推理 - AcWingarrow-up-right

另外本题还需要能够熟练运用字符串的处理方法。而且本题拥有大量输入数据,对于java语言来说应该使用一种更快速的获取输入的方法,我这里使用BufferedReader。

了解了思路之后本题看起来很简单,但是实际操作起来却总会遇到问题,也就是大家常说的“这题很麻烦”,还希望大家能够静下心来解决遇到的问题。

字符串处理简介

在本题中需要处理字符串,这里就来简单介绍几种常见的字符串处理方法,如果熟悉这部分的话可以略过这个环节直接看下面的代码。

1.基本操作函数

2.字符串比较

我们查字典时拼音chen是在cheng之前,这两个读音也在chun之前。

而字符串的比较和上面的字典序是类似的,只是每个字符之间的比较是按照字符集来进行的,字符集通常我们用ASCII和Unicode,一般的问题里ASCII比较多。

3.字符串输出

我们可以把一个数组转化为字符串,然后以字符串的形式输出,数组可以是byte类型也可以是char类型。

4.类型转换

5.字符串查找

首先是查找特定字符的出现位置

6.字符串截取

7.替换和修改

实现代码

Last updated