C++趣味算法之侦探推理

 更新时间:2021年12月02日 09:26:25   作者:指北针_N  
本文详细讲解了C++趣味算法之侦探推理,文中通过示例代码介绍的非常详细。对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

题目描述

明明同学最近迷上了侦探漫画《柯南》并沉醉于推理游戏之中,于是他召集了一群同学玩推理游戏。游戏的内容是这样的,明明的同学们先商量好由其中的一个人充当罪犯(在明明不知情的情况下),明明的任务就是找出这个罪犯。接着,明明逐个询问每一个同学,被询问者可能会说:

证词内容:

I am guilty.

I am not guilty.

XXX is guilty.

XXX is not guilty.

Today is XXX

证词含义:

我是罪犯

我不是罪犯

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

xxx 不是罪犯

今天是xxx ( xxx 表示星期几,是 Monday Tuesday wednesday Thursday Fnday Saturday 其中之一) 

证词中出现的其他话,都不列入逻辑推理的内容。明明所知道的是,他的同学中有 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

运行限制

最大运行时间:1s

最大运行内存:128M

算法实现

/*
大模拟问题
*/
 
#include <bits/stdc++.h>
using namespace std;
 
//m:总人数  n:始终说谎人数  p:说话的总数
int m, n,  p;
//judge[i]:第i句话是真是假,真1假-1不清楚0  w[i]:第i局话是编号多少的的人说的
int judge[21], w[200];
//err:矛盾标记  nx:当前可能的罪犯
int err,  nx;
//name[i]:所有人名字(编号为1~m)  say[i]:所有人说的话  day[i]:所有星期几名字
string name[100], say[200];
string day[10] = {"0", "Today is Sunday.", "Today is Monday.",
                  "Today is Tuesday.", "Today is Wednesday.", "Today is Thursday.",
                  "Today is Friday.", "Today is Saturday.",
                 };
 
//sset函数标记一个人说话真假
void sset(int who, int x) {
	if (judge[who] == -x)
		err = 1; //如果一个人既说真话又说假话,则矛盾
	else
		judge[who] = x;
}
 
int main() {
	cin >> m >> n >> p;
	for (int i = 1; i <= m; i++) {
		cin >> name[i];
	}
	for (int i = 1; i <= p; i++) {
		string nm;
		cin >> nm; //输入说这句话人的名字
		nm.erase(nm.end() - 1); //删除nm中冒号,便于判断这句话编号多少的的人说的
		for (int j = 1; j <= m; j++) {
			if (name[j] == nm)
				w[i] = j;
		}
		getline(cin, say[i]);
		say[i].erase(say[i].begin()); //删除say[i]中的起始空格
	}
 
	for (int td = 1; td <= 7; td++) { //暴力枚举今天是星期几
		for (int px = 1; px <= m; px++) { //暴力枚举罪犯编号是几号
			err = 0; //清除标记
			memset(judge, 0, sizeof(judge)); //初始化为不清楚真假
			//依次判断每一句说的话
			for (int i = 1; i <= p; i++) {
				int who = w[i]; //说这句话人的编号
				//如果一个人是罪犯,并且说自己是罪犯,则说的就是真话,否则就是假话
				if (say[i] == "I am guilty.")
					sset(who, px == who ? 1 : -1);
				//如果一个人不是罪犯,并且说自己不是罪犯,则说的就是真话,否则就是假话
				if (say[i] == "I am not guilty.")
					sset(who, px != who ? 1 : -1);
				//如果一个人说今天是星期几,说对了就是真话,说错了就是假话
				for (int j = 1; j <= 7; j++) {
					if (say[i] == day[j])
						sset(who, j == td ? 1 : -1);
				}
				//如果一个人说其他人不是罪犯,说对了就是真话,说错了就是假话
				for (int j = 1; j <= m; j++) {
					if (say[i] == name[j] + " is guilty.")
						sset(who, j == px ? 1 : -1);
					if (say[i] == name[j] + " is not guilty.")
						sset(who, j != px ? 1 : -1);
				}
			}
			int cnt = 0; //说假话的人数
			int no = 0; //不清楚真假的的人数
			for (int i = 1; i <= m; i++) {
				if (judge[i] == -1) //假
					cnt++;
				if (judge[i] == 0) //不清楚
					no++;
			}
			//如果出现Impossible的情况,err = 1,出现矛盾
			//如果cnt<=n<=cnt+no,即假设合理
			if (!err && cnt <= n && cnt + no >= n) {
				if (nx && nx != px) { //如果出现了两个合理的罪犯
					cout << "Cannot Determine";
					return 0;
				} else {
					nx = px;
				}
			}
		}
	}
	if (!nx)
		cout << "Impossible";
	else
		cout << name[nx];
 
	return 0;
}

以上所述是小编给大家介绍的C++趣味算法之侦探推理,希望对大家有所帮助。在此也非常感谢大家对脚本之家网站的支持!

相关文章

  • 详解C语言的预处理效果

    详解C语言的预处理效果

    这篇文章主要为大家介绍了C语言的预处理效果,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2021-12-12
  • 从c++标准库指针萃取器谈一下traits技法(推荐)

    从c++标准库指针萃取器谈一下traits技法(推荐)

    本篇文章基于gcc中标准库源码剖析一下标准库中的模板类pointer_traits,并且以此为例理解一下traits技法,对c++ traits技法源码分析感兴趣的朋友跟随小编一起看看吧
    2021-07-07
  • C语言简单实现三子棋游戏

    C语言简单实现三子棋游戏

    这篇文章主要为大家详细介绍了C语言简单实现三子棋游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-08-08
  • C语言如何实现Unix时间戳与本地时间转化

    C语言如何实现Unix时间戳与本地时间转化

    这篇文章主要介绍了C语言如何实现Unix时间戳与本地时间转化的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-03-03
  • 浅谈C++的浅拷贝出现的错误

    浅谈C++的浅拷贝出现的错误

    下面小编就为大家带来一篇浅谈C++的浅拷贝出现的错误。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-01-01
  • C语言数组和指针的问题一道非常值得深思的笔试题

    C语言数组和指针的问题一道非常值得深思的笔试题

    今天小编就为大家分享一篇关于C语言数组和指针的问题一道非常值得深思的笔试题,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2018-12-12
  • C++ stringstream格式化输出输入详情

    C++ stringstream格式化输出输入详情

    这篇文章主要介绍了C++ stringstream格式化输出输入,首先string str; cin>>str;遇到空格结束;于是乎产生了getline(),可与得到一行字符串;空格自动去掉,下面来看看文章的详细内容,需要的小伙伴可以参考一下
    2021-11-11
  • C语言实现二叉链表存储

    C语言实现二叉链表存储

    这篇文章主要为大家详细介绍了C语言实现二叉链表存储的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-08-08
  • Ubuntu配置sublime text 3的c编译环境的具体步骤

    Ubuntu配置sublime text 3的c编译环境的具体步骤

    下面小编就为大家带来一篇Ubuntu配置sublime text 3的c编译环境的具体步骤。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-03-03
  • C语言常见排序算法之交换排序(冒泡排序,快速排序)

    C语言常见排序算法之交换排序(冒泡排序,快速排序)

    这篇文章主要介绍了C语言常见排序算法之交换排序(冒泡排序,快速排序),冒泡排序即Bubble Sort,类似于水中冒泡,较大的数沉下去,较小的数慢慢冒起来,假设从小到大,即为较大的数慢慢往后排,较小的数慢慢往前排
    2022-07-07

最新评论