洛谷P1039 侦探推理(模拟)

洛谷P1039 侦探推理(模拟)

侦探推理

题目描述

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

证词中出现的其他话,都不列入逻辑推理的内容。

明明所知道的是,他的同学中有NN个人始终说假话,其余的人始终说真。

现在,明明需要你帮助他从他同学的话中推断出谁是真正的凶手,请记住,凶手只有一个!

输入输出格式

输入格式:

 

输入由若干行组成,第一行有三个整数,M(1≤M≤20)M(1M20)、N(1≤N≤M)N(1NM)和P(1≤P≤100)P(1P100);MM是参加游戏的明明的同学数,NN是其中始终说谎的人数,PP是证言的总数。

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

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

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

 

输出格式:

 

如果你的程序能确定谁是罪犯,则输出他的名字;如果程序判断出不止一个人可能是罪犯,则输出 "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??
输出样例#1: 复制
MIKE

题目类型:

模拟题,暴力

思路:

用suspect[i,j]记录i对j是不是凶手的判断:

suspect[i,j]=1时,i认为j是凶手;

suspect[i,j]=-1时,i认为j不是凶手;

peo[i]记录第i个人的名字

用a[i].day记录第i个人认为今天是星期几

然后咱枚举犯人(guilty)和星期几(today)来对每个人逐一判断(不会傻到用组合去判断谁说假话吧0.0):

咱们用f数组记录我们对第i个人的判断

如果f[i]=1说明他说真话,f[i]=2说明他说假话(注意清0)

由于全部人分为三类:只说真话,只说假话,说废话

所以如果有人既说过真话又说过假话这种情况是不会存在的,直接判断出false就行

然后分类讨论:

1、suspect[i,guilty]=1

说明i说真话,如果f[i]=2即对于这组guilty、today他说过假话,矛盾,返回false 不然的话他说真话f[i]=1

2、suspect[i,guilty]=2

说明i说假话,如果f[i]=1即他说过真话,矛盾返回false,不然f[i]=2

3、对于i对除了guilty以外所有人(j<>guilty)的判断

(1)如果suspect[i,j]=1 说明他认为j是凶手,是假话,如果f[i]=1即他说过真话,矛盾返回false,不然f[i]=2

(2)如果suspect[i,j]=-1说明他认为j不是凶手,是真话,如果f[i]=2即他说过假话,矛盾返回false 不然的f[i]=1

4、a[i].day>0即他说过今天是星期几并且a[i].j<>today 即他说错了,是假话,如果f[i]=1即他说过真话,矛盾返回false,不然f[i]=2

然后我们统计f[i]=2即说谎的人数t1和f[i]=0即不确定的人数t2

然后关键来了,究竟什么是满足要求的情况呢?

t1不一定非要严格等于说谎人数m,因为有人不确定,而这批不确定的人中可能也有人是说谎者只是他没说有用的而已。所以满足的条件是 (t1<=m)and  (t1+t2>=m)

由于结果有三种情况:

1、真相只有一个——有且只有1个满足条件的犯人,直接输出名字

2、他们同伙作案——有大于1个人满足他是犯人的条件,输出Cannot Determine

3、错误的嫌疑人——没有人满足犯人的条件,输出 Impossible

所以我们如果判断出一个人是犯人不要着急输出,

而是记录我们找到的满足犯人条件的人数t,以及他的名字ans,

每找到一个就t+1,ans更新为他的名字

一个小细节就是我们是犯人和星期同时枚举的,所以如果我们判断出一个人是犯人就可以直接去判断下一个人不然这一个人会加好几遍。还有判断的时候是区分大小写的。

 

#include<cstdio>
#include<map>
#include<iostream>
#include<string>
using namespace std;
int n,m,p;
/*用suspect[i,j]记录i对j是不是凶手的判断:
suspect[i,j]=1时,i认为j是凶手;
suspect[i,j]=-1时,i认为j不是凶手;*/
int s[22][102];
int l[22];
int d[22][102];
int day[22][102];
int gu[22];
map<string,int>t;//用来记名字用 
map<int,string>h;
string u,v[102],g[102],q[102],bb;
int main()
{
    cin>>m>>n>>p;
    for(int i=1;i<=m;i++)
    {
        cin>>u;
        t[u]=i;
        h[i]=u;
    }
    for(int i=1;i<=p;i++)
    {
        //cout<<"i "<<i<<endl;
        int y,ll=1;
        u="";
        
        //一个个单词读进来,遇到.?!表示语句结束 
        while(u[ll-1]!='.'&&u[ll-1]!='?'&&u[ll-1]!='!')
        {
            cin>>bb;
            //cout<<"bb "<<bb<<endl;
            if(ll>1) u+=' ';
            u+=bb;
            ll=u.length(); //ll表示整一句的字母数 
        }
        //cout<<"u "<<u<<endl;
        //cout<<"ll "<<ll<<endl;
        //把证词人名整理好放进v数组里 
        for(int j=0;j<ll&&u[j]!=':';j++)
        {
            v[i]+=u[j];
            y=j;
         }
         int jj=t[v[i]];//这句话是编号为jj的人说的
         int b=-1,nn=0,uu=0;
         //b表示第一个单词是人名的话,存放这是第几个人 
         //y用来标记人名到底几个字符位置 
         //uu表示输入的句子是不是关于Today is的 
         //g用于把句子拼接起来 
        for(int j=y+3;j<ll;j++)
        {
            //如果是关于星期的,就把星期几放到q里 
            if(!uu) g[i]+=u[j];else q[i]+=u[j];          
            //查一查此时的g[i]是不是某个人的名字,MIKE is guilty.
            //是的话吧第几个人k放到b里,h[k]返回第k个人名字符串 
            if(!nn&&!uu&&u[j+1]==' ')for(int k=1;k<=m;k++)if(h[k]==g[i]){b=k;break;}
            //第一个单词是人名 
            if(b!=-1&&!nn&&!uu){g[i]=u[j+2];j+=2;nn=1;}
            if(g[i]=="Today is ")uu=1;
        }
        //l[jj]表示第jj个人的第几个观点 
        //s[jj][l[jj]]=3,d[jj][l[jj]++]=b表示第jj个人的第 l[jj]++个观点认为b是凶手 
        if(g[i]=="I am guilty.")s[jj][l[jj]++]=1;
        if(g[i]=="I am not guilty.")s[jj][l[jj]++]=2;
        if(g[i]=="is guilty."){s[jj][l[jj]]=3;d[jj][l[jj]++]=b;}
        if(g[i]=="is not guilty."){s[jj][l[jj]]=4;d[jj][l[jj]++]=b;}
        if(g[i]=="Today is ")
        {
            //s[jj][l[jj]]=5,day[jj][l[jj]++]=1表示第jj个人的第 l[jj]++个观点认为今天是星期一 
            s[jj][l[jj]]=5;
            if(q[i]=="Monday.")day[jj][l[jj]++]=1;
            if(q[i]=="Tuesday.")day[jj][l[jj]++]=2;
            if(q[i]=="Wednesday.")day[jj][l[jj]++]=3;
            if(q[i]=="Thursday.")day[jj][l[jj]++]=4;
            if(q[i]=="Friday.")day[jj][l[jj]++]=5;
            if(q[i]=="Saturday.")day[jj][l[jj]++]=6;
            if(q[i]=="Sunday.")day[jj][l[jj]++]=7;
        }
    }
    //cout<<"输入完毕"<<endl; 
    //最后遍历每个人假设他是凶手 
    for(int i=1;i<=m;i++)
    {
        //遍历今天星期几 
        for(int j=1;j<=7;j++)
        {
            //针对每一个的证词判断他说真话还是假话 
            //fa统计说谎话的人
            //abc统计不真不假的人 
            int fa=0,T,F,abc=0;
            for(int k=1;k<=m;k++)
            {
                T=F=0;
                //l[k]表示第k个人说的证词条数(信息数) 
                for(int kk=0;kk<l[k];kk++)
                {
                    //关于凶手 
                   if(s[k][kk]==1)if(i==k)T=1;else F=1;
                   if(s[k][kk]==2)if(i!=k)T=1;else F=1;
                   if(s[k][kk]==3)if(i==d[k][kk])T=1;else F=1;
                   if(s[k][kk]==4)if(i!=d[k][kk])T=1;else F=1;
                   //关于星期 
                   if(s[k][kk]==5)if(j==day[k][kk])T=1;else F=1;                        
                 } 
                 if(T&&F)break;
                if(F) fa++;
                if(!T&&!F)abc++;
            }
            if(T&&F)continue;
            //如果说谎话的人正好n个或<=n个 ,
            //且说谎话的人和不真不假的人比n个多 
            //gu[i]=1表示第i个人是凶手成立 
            if(fa==n||fa<=n&&fa+abc>=n)gu[i]=1;
        }
     } 
    //ans统计有多少人可能是凶手 
    int ans=0;
    for(int i=1;i<=m;i++)if(gu[i])ans++;
    if(!ans)printf("Impossible");
    else if(ans>1)printf("Cannot Determine");
    else for(int i=1;i<=m;i++)if(gu[i]){cout<<h[i];break;}
    return 0;        
}
    
    
    

 

---------------------
作者:WhiStLenA
来源:CSDN
原文:https://blog.csdn.net/whistlena/article/details/78240157

posted on 2019-04-26 23:02 蔡军帅_ACM 阅读(...) 评论(...) 编辑 收藏
posted on 2019-04-26 23:02  蔡军帅  阅读(234)  评论(0编辑  收藏  举报