AcWing 478. 侦探推理 枚举+模拟

5 篇文章 0 订阅

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

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

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

证词内容 证词含义

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中的一个)

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

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

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

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

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

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

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

数据范围
1≤M≤20,
1≤N≤M,
1≤P≤100
输入样例:

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

思路
首先我们枚举所有的可能事实,比如说事实今天星期 i,凶手 是 j.
如果同一天内存在两个罪犯,我们无法判读罪犯是谁,
如果一天存在一个罪犯,我们可以判断j是罪犯。
如果都不成立,说明找不到罪犯。

最细节的还是要看代码
代码如下:

y总的视频讲的很细节。链接

但是我觉得这次y总写的代码不是很好,有很多逻辑对不上。我写了一遍这样的代码

#include<iostream>
#include<algorithm>
#include<cstring>

using namespace std;

const int N=25,M=110;

string name[N];
pair<int,string> sentence[M];
int n,m,p;
int st[N];


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

int get_id(string x)
{
    for(int i=0;i<m;i++)
    if(name[i]==x)
    return i;
}
//

//0 为 真 ,1为假话
int judge(int day,int badman,int id,string word)
{
    if(word=="I am guilty.")
    return id!=badman;
    if(word=="I am not guilty.")
    return id==badman;
    for(int i=0;i<m;i++)
    if(name[i]+" is guilty."==word)
    return i!=badman;
    else if(name[i]+" is not guilty."==word)
    return i==badman;
    for(int i=0;i<7;i++)
    if(word==week[i])
    return i!=day;
    return -1;
}





bool check(int day, int badman)
{
    memset(st,-1,sizeof st);
    
    for(int i=0;i<p;i++)
    {
        pair<int,string> sen=sentence[i];
        int k=sen.first;
        int t=judge(day,badman,sen.first,sen.second);
        if(t==0)
        {
            if(st[k]==-1) st[k]=0;
            else if(st[k]==1) return false;
        }
        else if(t==1)
        {
            if(st[k]==-1) st[k]=1;
            else if(st[k]==0) return false;
        }
    }
    
    int fake=0,other=0;
    for(int i=0;i<m;i++)
    if(st[i]==1)
    fake++;
    else if(st[i]==-1)
    other++;
    return fake<=n&&n<=fake+other;//我觉得这一步并不是很好
}


int main(void)
{
    
    scanf("%d %d %d",&m,&n,&p);
    for(int i=0;i<m;i++) cin>>name[i];
    for(int i=0;i<p;i++)
    {
        string sper;
        cin>>sper;
        sper.erase(sper.end()-1);
        int k=get_id(sper);
        string word;
        getline(cin,word);
        word.erase(word.begin());
        sentence[i]=make_pair(k,word);
    }
    int cnt=0,p=-1;
    for(int day=0;day<7;day++)
    {
        for(int i=0;i<m;i++)
        {
            if(check(day,i))
            cnt++,p=i;
        }
        if(cnt==1) break;//找到凶手了
    }
    if(cnt==1)
    {
        cout<<name[p];
    }
    else
    {
    if(p==-1)    //说明没找到过凶手
    puts("Impossible");
    else//说明枚举的次数多,判断不过来
    puts("Cannot Determine");
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值