侦探推理问题

前言

对于侦探推理问题,我看到的都是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")  # 如果没有可能的罪犯,则输出“不可能”





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值