1.4 侦探推理

来源:NOIP2003提高组 https://ac.nowcoder.com/acm/contest/251/B

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

证词中出现的其他话,都不列入逻辑推理的内容。
明明所知道的是,他的同学中有N个人始终说假话,其余的人始终说真。
现在,明明需要你帮助他从他同学的话中推断出谁是真正的凶手,请记住,凶手只有一个!

输入描述:
输入由若干行组成,第一行有二个整数,M(1≤M≤20)、N(1≤N≤M)和P(1≤P≤100);M是参加游戏的明明的同学数,N是其中始终说谎的人数,P是证言的总数。接下来M行,每行是明明的一个同学的名字(英文字母组成,没有主格,全部大写)。
往后有P行,每行开始是某个同学的名宇,紧跟着一个冒号和一个空格,后面是一句证词,符合前表中所列格式。证词每行不会超过250个字符。
输入中不会出现连续的两个空格,而且每行开头和结尾也没有空格。

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

示例1
输入

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

题解: 该题牛客直播讲解链接:https://www.nowcoder.com/study/live/248/1/5

接下来说一下我的想法:m n p,分别是人名个数,说假话的人数,以及说的话数量

注意:有些人可能没有说话,so有可能没有说话的都是假话,也有可能都是真话

所以:统计数据中得出的说假话的人数fake,以及没有说话人数other
若other都是真话,则说假话人数最小值应该为 fake ,
若other都是假话,则说假话人数最大值应该为 fake + other
so 假话人数应该介于最大最小值之间 即:fake <= n && n <= fake + other

这道题需要模拟假设某人是凶手,今天是星期几。在这两个限制条件下进行判断每句话的真假,进而得知说这句话的人是否诚实。
判断完所有话,n的值并没有介于: fake <= n && n <= fake + other则说明该假设不成立。
n的值若符合条件,则返回main函数进行计数器++,记录当前的凶手

判断说的话:则需要判断前四种关于罪犯的话以及关于星期几的话
类似于这种的:xxx is guilty.则需要将这句话的人物和话分开,调用函数get()函数进行分离,可以用str.find(" is guilty."); 用find函数找一下是不是这种话。
前四种关于罪犯的话不是,则可能是关于星期几的,for循环判断的到传入的星期几是否与开始假设的星期相同
这五种若都不是,则是废话直接返回。

知识点积累:关于字符串string的知识

1、str.find()函数

查找某个字符,或者查找某一串字符是否存在,存在则返回当前查找字符或字符串的起始位置下标;

eg:      str.find(':');       str.find(" is not guilty.");

2、str.substr()函数,

截取某一段字符串并返回该字符串,参数为两个,第一个起始位置下标,第二个终止位置下标,若不写第二个参数则默认为末尾。

	eg:   string person = str.substr(0,t);     string sent = str.substr(t+2);

3、memset()函数

头文件cstring 参数:地址,初始化的值(只能初始化为-1和0),长度

	  int  state[100];
	  memset(state,-1,sizeof(state));

具体题解:看代码中的注释。。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <vector>
#include <queue>
#include <stack>
#include <algorithm>
using namespace std;
typedef long long ll;
#define maxn 1000005
#define mod 7654321
#define NIL -1

const int N = 110;

int m,n,p;

//存储姓名和说的话
string name[N],words[N];

string weekday[7] =
{
    "Today is Monday.",
    "Today is Tuesday.",
    "Today is Wednesday.",
    "Today is Thursday.",
    "Today is Friday.",
    "Today is Saturday.",
    "Today is Sunday."
};

int state[N];//每个人的状态 真:0 假:1 废话:-1

//返回当前名字的人物编号
int get_person(string str)
{
    for(int i=0;i<m;i++)
    {
        if(str == name[i])
            return i;
    }
    return -1;
}


//将话中的人物和话分离,匹配人物编号与话
pair<int,string> get(string str)
{
    int t = str.find(':');
    string person = str.substr(0,t);
    string sent = str.substr(t+2);
    int i = get_person(person);

    return pair<int,string>(i,sent);
    //或者:return {i,sent};
}

//返回若当前人物为凶手,当前day条件下每句话的状态 真:0 假:1 废话:-1
int get_state(int bad_man,int day,int person,string str)
{
    if(str == "I am guilty.")
    {
        //若当前假设凶手 和当前人物陈述一样 则为真话
        if(bad_man == person) return 0;
        return 1;
    }
    if(str == "I am not guilty.")
    {
        //若当前假设凶手 和当前人物陈述一样 则为真话
        if(bad_man != person) return 0;
        return 1;
    }
    //不存在返回-1
    int t = str.find(" is guilty.");
    if(t != -1)
    {
        //获得当前话中的人物
        string people = str.substr(0,t);
        int p = get_person(people);
        //若当前假设凶手 和当前人物陈述一样 则为真话
        if(p == bad_man) return 0;
        return 1;
    }
    t = str.find(" is not guilty.");
    if(t != -1)
    {
        //获得当前话中的人物
        string people = str.substr(0,t);
        int p = get_person(people);
        //若当前假设凶手 和当前人物陈述一样 则为真话
        if(bad_man != p) return 0;
        return 1;
    }

    //最后判断星期几
    for(int i=0;i<7;i++)
    {
        //若当前话str是说的周几则找出当前周几的编号
        if(weekday[i] == str)
        {
            if(i == day) return 0;
            return 1;
        }
    }

    //否则说的是废话,返回-1
    return -1;
}

bool check(int bad_man,int day)
{
    //头文件:cstring 参数:地址,初始化的值(只能初始化为-1和0),长度
    //默认为废话-1,调用一次,初始化一次人物状态
    memset(state,-1,sizeof(state));
    for(int i = 0 ; i < p ; i++)
    {
        pair<int,string> t = get(words[i]);
        int person = t.first;
        //s 存储当前话的状态
        int s = get_state(bad_man,day,person,t.second);

        //判断是否始终说真话或始终说假话

        if(s == 0)//若是真话
        {
            if(state[person] == 1)//若当前人物之前说过假话返回false
                return false;
            state[person] = s;//更新当前人物的状态
        }
        else if(s == 1)//若是假话
        {
            if(state[person] == 0)//若当前人物之前说过真话返回false
                return false;
            state[person] = s;//更新当前人物的状态
        }
    }
    //统计m个人中说假话人数和没有说话的人数
    int fake = 0,other = 0;
    for(int i = 0 ; i < m ; i++)
    {
        if(state[i] == 1) fake++;
        else if(state[i] == -1) other++;
    }

    //若other都是真话,则说假话人数最小值应该为 fake ,
    //若other都是假话,则说假话人数最大值应该为 fake + other
    //so 假话人数应该介于最大最小值之间
    if(fake <= n && n <= fake + other)
        return true;
    else
        return false;
}

int main()
{
    cin>>m>>n>>p;
    for(int i=0;i<m;i++)
        cin>>name[i];

    getchar();//接受回车
    for(int i=0;i<p;i++)
        getline(cin,words[i]);

    //依次枚举凶手、星期几以及句子
    int cnt = 0,p;
    for(int i=0;i<m;i++)//凶手
    {
        for(int day=0;day<7;day++)//星期几
        {
            //枚举 若当前人物为凶手,说假话人数的范围(fake,fake+other)是否可能有 n
            if(check(i,day))
            {
                p = i;//存储当前凶手编号
                cnt++;//记录当前可能凶手个数
                break;
            }
        }
    }

    if(cnt == 0) puts("Impossible");
    else if(cnt == 1) cout<<name[p]<<endl;
    else puts("Cannot Determine");

    return 0;
}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值