前言
对于侦探推理问题,我看到的都是c或则c++的答案,而我想用python代码去解决这个问题,接下来我们一起来看看吧!!!
问题
明明同学最近迷上了侦探漫画《柯南》并沉醉于推理游戏之中,于是他召集了一群同学玩推理游戏。游戏的内容是这样的,明明的同学们先商量好由其中的一个人充当罪犯(在明明不知情的情况下),明明的任务就是找出这个罪犯。接着,明明逐个询问每一个同学,被询问者可能会说:
证词中出现的其他话,都不列入逻辑推理的内容。
明明所知道的是,他的同学中有N个人始终说假话,其余的人始终说真。
现在,明明需要你帮助他从他同学的话中推断出谁是真正的凶手,请记住,凶手只有一个!
输入描述:
输入由若干行组成,第一行有二个整数,M(1≤M≤20)、N(1≤N≤M)和P(1≤P≤100);M是参加游戏的明明的同学数,N是其中始终说谎的人数,P是证言的总数。接下来M行,每行是明明的一个同学的名字(英文字母组成,没有主格,全部大写)。
往后有P行,每行开始是某个同学的名宇,紧跟着一个冒号和一个空格,后面是一句证词,符合前表中所列格式。证词每行不会超过250个字符。
输入中不会出现连续的两个空格,而且每行开头和结尾也没有空格。
输出描述:
如果你的程序能确定谁是罪犯,则输出他的名字;如果程序判断出不止一个人可能是罪犯,则输出 Cannot Determine;如果程序判断出没有人可能成为罪犯,则输出 Impossible。
示例:
输入:
3 1 5
MIKE
CHARLES
KATE
MIKE: I am guilty.
MIKE: Today is Sunday.
CHARLES: MIKE is guilty.
KATE: I am guilty.
KATE: How are you??
输出:
MIKE
问题分析:
解题思路可以分为以下几个步骤:
1. 输入数据解析
读取基本信息:首先读取同学的数量 `m`、说谎人数 `n` 和证词数量 `P`。
读取同学名字:读取每个同学的名字,并将名字映射为索引,方便后续处理。
读取证词:读取每一条证词,每条证词包含说话者的名字和说话内容。
2. 证词解析
解析说话者和内容:对于每条证词,解析出说话者的名字和说话内容。
确定说话者索引:通过名字字典将说话者的名字转换为索引。
3. 逻辑判断
判断说话者是否说谎:根据说话内容和当前假设的罪犯及日期,判断说话者是否在说谎。
如果说话者说“我是罪犯”,则判断其是否为假设的罪犯。
如果说话者说“我不是罪犯”,则判断其是否为假设的罪犯。
如果说话者指控某人是罪犯或不是罪犯,判断被指控者是否为假设的罪犯。
如果说话者提到某一天,判断其提到的日期是否与假设的日期相符。
更新状态:根据判断结果更新说话者的状态(说真话或说谎)。
4. 状态检查
遍历假设条件:遍历每个同学,假设其为罪犯,并在一周的每一天中检查所有证词是否合理。
统计说谎人数:统计在当前假设下说谎的人数,判断是否符合给定的说谎人数 `n`。
判断合理性:如果在某个假设下,所有证词都合理且说谎人数符合要求,则认为该假设合理。
5. 结果输出
唯一罪犯:如果只有一个同学在所有假设下都合理,则确定其为罪犯并输出其名字。
无法确定:如果有多个同学在不同假设下都合理,则输出“无法确定”。
不可能:如果没有同学在任何假设下都合理,则输出“不可能”。
代码展示:
输入数据解析:
m, n, P = map(int, input().split()) # 读取同学数量、说谎人数和证词数量
names = [input() for _ in range(m)] # 读取同学的名字
name_dict = {name: i for i, name in enumerate(names)} # 将名字映射为索引
sentences = [input() for _ in range(P)] # 读取证词
找同学的索引:
# 定义函数,根据名字字典查找名字对应的索引,如果不存在则返回-1
def get_person(name_dict,str_name):
return name_dict.get(str_name,-1)
定义一周的每一天:
# 定义一周的每一天
weekday = [
"Today is Monday.",
"Today is Tuesday.",
"Today is Wednesday.",
"Today is Thursday.",
"Today is Friday.",
"Today is Saturday.",
"Today is Sunday."
]
证词解析:
# 定义函数,解析句子,返回说话者索引和陈述内容
def get(str_sentence):
t = str_sentence.find(":") # 找到冒号的位置
person_id = get_person(name_dict, str_sentence[:t]) # 获取说话者的名字并转换为索引
return person_id, str_sentence[t + 2:] # 返回说话者索引和陈述内容
逻辑判断:
# 定义函数,根据当前假设的罪犯和日期,判断说话者是否在说谎
def get_state(bad_man, day, saying_person, line):
print(bad_man, day, saying_person, line)
if line == "I am guilty.": # 如果说话者说“我是罪犯”
return 0 if bad_man == saying_person else 1 # 如果说话者是假设的罪犯,则返回0(说真话),否则返回1(说谎)
if line == "I am not guilty.": # 如果说话者说“我不是罪犯”
return 1 if bad_man == saying_person else 0 # 如果说话者是假设的罪犯,则返回1(说谎),否则返回0(说真话)
t = line.find(" is guilty.") # 查找“是罪犯”的位置
if t != -1: # 如果找到
p = get_person(name_dict, line[:t]) # 获取被指控者的名字并转换为索引
return 0 if p == bad_man else 1 # 如果被指控者是假设的罪犯,则返回0(说真话),否则返回1(说谎)
t = line.find(" is not guilty.") # 查找“不是罪犯”的位置
if t != -1: # 如果找到
p = get_person(name_dict, line[:t]) # 获取被指控者的名字并转换为索引
return 0 if p != bad_man else 1 # 如果被指控者不是假设的罪犯,则返回0(说真话),否则返回1(说谎)
for i, weekday_str in enumerate(weekday): # 遍历一周的每一天
if weekday_str == line: # 如果说话者提到的是某一天
return 0 if i == day else 1 # 如果说话者提到的日期与假设的日期相符,则返回0(说真话),否则返回1(说谎)
return -1 # 如果无法判断说话者是否在说谎,则返回-1
状态检查:
# 定义函数,检查在给定的罪犯和日期假设下,所有陈述是否合理
def check(bad_man, day):
state = [-1] * m # 初始化状态数组,-1表示未知
for sentence in sentences: # 遍历所有证词
person_id, line = get(sentence) # 解析证词
s = get_state(bad_man, day, person_id, line) # 判断说话者是否在说谎
if s == 0: # 如果说话者在说真话
if state[person_id] == 1: # 如果之前认为说话者在说谎,则矛盾
return False
state[person_id] = s # 更新说话者的状态为说真话
elif s == 1: # 如果说话者在说谎
if state[person_id] == 0: # 如果之前认为说话者在说真话,则矛盾
return False
state[person_id] = s # 更新说话者的状态为说谎
print(state)
fake = sum(1 for s in state if s == 1) # 统计说谎的人数
other = sum(1 for s in state if s == -1) # 统计未知状态的人数
return fake <= n and fake + other >= n # 判断是否符合说谎人数的条件
结果输出:
cnt = 0 # 初始化可能的罪犯数量
p = -1 # 初始化罪犯的索引
for i in range(m): # 遍历每个同学
flag = any(check(i, j) for j in range(7)) # 检查在每个可能的日期下,所有证词是否合理
if flag:
cnt += 1 # 如果合理,则增加可能的罪犯数量
p = i # 更新罪犯的索引
# 根据检查结果,判断罪犯并输出相应的结果
if cnt == 1:
print(names[p]) # 如果只有一个可能的罪犯,则输出其名字
elif cnt > 1:
print("Cannot Determine") # 如果有多个可能的罪犯,则输出“无法确定”
else:
print("Impossible") # 如果没有可能的罪犯,则输出“不可能”
全部的代码:
# import sys
# 定义函数,根据名字字典查找名字对应的索引,如果不存在则返回-1
def get_person(name_dict,str_name):
return name_dict.get(str_name,-1)
# 定义函数,解析句子,返回说话者索引和陈述内容
def get(str_sentence):
t = str_sentence.find(":") # 找到冒号的位置
person_id = get_person(name_dict, str_sentence[:t]) # 获取说话者的名字并转换为索引
return person_id, str_sentence[t + 2:] # 返回说话者索引和陈述内容
# 定义函数,根据当前假设的罪犯和日期,判断说话者是否在说谎
def get_state(bad_man, day, saying_person, line):
print(bad_man, day, saying_person, line)
if line == "I am guilty.": # 如果说话者说“我是罪犯”
return 0 if bad_man == saying_person else 1 # 如果说话者是假设的罪犯,则返回0(说真话),否则返回1(说谎)
if line == "I am not guilty.": # 如果说话者说“我不是罪犯”
return 1 if bad_man == saying_person else 0 # 如果说话者是假设的罪犯,则返回1(说谎),否则返回0(说真话)
t = line.find(" is guilty.") # 查找“是罪犯”的位置
if t != -1: # 如果找到
p = get_person(name_dict, line[:t]) # 获取被指控者的名字并转换为索引
return 0 if p == bad_man else 1 # 如果被指控者是假设的罪犯,则返回0(说真话),否则返回1(说谎)
t = line.find(" is not guilty.") # 查找“不是罪犯”的位置
if t != -1: # 如果找到
p = get_person(name_dict, line[:t]) # 获取被指控者的名字并转换为索引
return 0 if p != bad_man else 1 # 如果被指控者不是假设的罪犯,则返回0(说真话),否则返回1(说谎)
for i, weekday_str in enumerate(weekday): # 遍历一周的每一天
if weekday_str == line: # 如果说话者提到的是某一天
return 0 if i == day else 1 # 如果说话者提到的日期与假设的日期相符,则返回0(说真话),否则返回1(说谎)
return -1 # 如果无法判断说话者是否在说谎,则返回-1
# 定义函数,检查在给定的罪犯和日期假设下,所有陈述是否合理
def check(bad_man, day):
state = [-1] * m # 初始化状态数组,-1表示未知
for sentence in sentences: # 遍历所有证词
person_id, line = get(sentence) # 解析证词
s = get_state(bad_man, day, person_id, line) # 判断说话者是否在说谎
if s == 0: # 如果说话者在说真话
if state[person_id] == 1: # 如果之前认为说话者在说谎,则矛盾
return False
state[person_id] = s # 更新说话者的状态为说真话
elif s == 1: # 如果说话者在说谎
if state[person_id] == 0: # 如果之前认为说话者在说真话,则矛盾
return False
state[person_id] = s # 更新说话者的状态为说谎
print(state)
fake = sum(1 for s in state if s == 1) # 统计说谎的人数
other = sum(1 for s in state if s == -1) # 统计未知状态的人数
return fake <= n and fake + other >= n # 判断是否符合说谎人数的条件
# 读取输入数据
m, n, P = map(int, input().split()) # 读取同学数量、说谎人数和证词数量
names = [input() for _ in range(m)] # 读取同学的名字
name_dict = {name: i for i, name in enumerate(names)} # 将名字映射为索引
sentences = [input() for _ in range(P)] # 读取证词
# 定义一周的每一天
weekday = [
"Today is Monday.",
"Today is Tuesday.",
"Today is Wednesday.",
"Today is Thursday.",
"Today is Friday.",
"Today is Saturday.",
"Today is Sunday."
]
cnt = 0 # 初始化可能的罪犯数量
p = -1 # 初始化罪犯的索引
for i in range(m): # 遍历每个同学
flag = any(check(i, j) for j in range(7)) # 检查在每个可能的日期下,所有证词是否合理
if flag:
cnt += 1 # 如果合理,则增加可能的罪犯数量
p = i # 更新罪犯的索引
# 根据检查结果,判断罪犯并输出相应的结果
if cnt == 1:
print(names[p]) # 如果只有一个可能的罪犯,则输出其名字
elif cnt > 1:
print("Cannot Determine") # 如果有多个可能的罪犯,则输出“无法确定”
else:
print("Impossible") # 如果没有可能的罪犯,则输出“不可能”