1. 程式人生 > >洛谷P1039 偵探推理(模擬)

洛谷P1039 偵探推理(模擬)

str close while 任務 suspect 輸入 http pos base

偵探推理

題目描述

明明同學最近迷上了偵探漫畫《柯南》並沈醉於推理遊戲之中,於是他召集了一群同學玩推理遊戲。遊戲的內容是這樣的,明明的同學們先商量好由其中的一個人充當罪犯(在明明不知情的情況下),明明的任務就是找出這個罪犯。接著,明明逐個詢問每一個同學,被詢問者可能會說:

技術分享圖片

證詞中出現的其他話,都不列入邏輯推理的內容。

明明所知道的是,他的同學中有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

洛谷P1039 偵探推理(模擬)