题目描述
明明同学最近迷上了侦探漫画《柯南》并沉醉于推理游戏之中,于是他召集了一群同学玩推理游戏。游戏的内容是这样的,明明的同学们先商量好由其中的一个人充当罪犯(在明明不知情的情况下),明明的任务就是找出这个罪犯。接着,明明逐个询问每一个同学,被询问者可能会说:
证词中出现的其他话,都不列入逻辑推理的内容。
明明所知道的是,他的同学中有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??
输出样例#1: 复制
MIKE
//记录每一句话是谁说的以及这句话的内容 //可以用map存人名对应的下标 //我们枚举每一个人i,假设i是罪犯 //然后枚举今天是星期几,用day表示 //然后判断有没有矛盾 //如何判断? //进行每一次判断的时候,先使所有人的状态不确定,也就是不知道他们会说真话假话 //TF[a]==-1是不确定,TF[a]=1是说真话,TF[a]=0是说假话 //T是说真话的人数,F是说假话的人数 //设罪犯为 i //设flag为这句话是真话还是假话,flag=1是真话,flag=0是假话 //id是说这句话的人 //枚举每一句话 // 看一下id以前的状态,如果状态不确定(TF==-1),就TF[id]=flag // 否则,如果和以前状态一样(TF[id]==flag),就没有矛盾, // TF[id]!=flag就是出现了矛盾(因为一个人始终直说一种话),判断不出来了,直接return去枚举下一个人是罪犯 //如果F>n或者T>m-n了,也就是说假话的人数超过了题目中给的人数,矛盾,return //如果找到了不止一个罪犯,输出"Cannot Determine",直接exit(0) //怎么知道这句话是真话假话? //①如果话里有 "I am guilty." // 那么看一下id是不是i,不是的话,就是在说假话 //②话里有"I am not guilty" // 看一下id是不是i,不是的话,就是在说真话,否则就是假话 //③话里有"xxx is guilty" // 如果xxx是i的话,就是真话,否则是假话 //④话里有"xxx is not guilty" // 如果xxx不是i的话,就是真话,否则是假话 //⑤话里有"Today is XXX" // 如果xxx与day一样,就是真话,否则是假话 #include<iostream> #include<cstdio> #include<cstring> #include<map> #include<algorithm> using namespace std; string S[10]= { "Today is Sunday.", "Today is Monday.", "Today is Tuesday.", "Today is Wednesday.", "Today is Thursday.", "Today is Friday.", "Today is Saturday.", }; int m,n,p; int T,F,ans; int TF[25]; struct Sen { int id; string s; }sen[105]; map<string,int> ma; bool judgeTF(int id,bool flag) //看一下有没有冲突,return 1 表示有冲突 { if(TF[id]==-1) //状态不确定 { TF[id]=flag; //赋状态 if(flag) //说真话的人数++ ++T; else //说假话的人数++ ++F; } else return TF[id]!=flag; //和之前的一不一样,一样返回0,不一样返回1 if(F>n||T>m-n) //说假话的人比n多或者是说真话的人比m-n多 return 1; return 0; } void judge(int id,string day) { memset(TF,-1,sizeof(TF)); //所有人都不知道说的是真话假话 T=F=0; //说真话、假话人数置0 string tmp; for(int i=1;i<=p;++i) { int pos=sen[i].s.find("I am guilty."); //pos为-1则没说这句话 if(~pos) { if(judgeTF(sen[i].id,sen[i].id==id)) //因为我们假设了id是罪犯,所以不是id的人就不是罪犯,就是在说假话 return; } pos=sen[i].s.find("I am not guilty"); if(~pos) { if(judgeTF(sen[i].id,sen[i].id!=id)) return; } pos=sen[i].s.find(" is guilty."); if(~pos) { tmp=sen[i].s; tmp.erase(pos,11); if(judgeTF(sen[i].id,ma[tmp]==id)) return; } pos=sen[i].s.find(" is not guilty."); if(~pos) { tmp=sen[i].s; tmp.erase(pos,15); if(judgeTF(sen[i].id,ma[tmp]!=id)) return; } pos=sen[i].s.find("Today is "); if(~pos) { if(judgeTF(sen[i].id,sen[i].s==day)) return; } } if(ans&&ans!=id) //找到了不止一个罪犯 { puts("Cannot Determine"); //不能确定 exit(0); } ans=id; //id是罪犯 } string s[25],name,a; int main() { scanf("%d%d%d",&m,&n,&p); for(int i=1;i<=m;++i) { cin>>s[i]; ma[s[i]]=i; //存名字标号 } for(int i=1;i<=p;++i) { cin>>name; //输入说话者 name.erase(name.length()-1,1); //把后边的冒号搞掉 getline(cin,a); a.erase(0,1); //把前边的空格搞掉 if(a[a.length()-1]==' '||a[a.length()-1]==' ') //把坑爹的换行符搞掉 a.erase(a.length()-1,1); sen[i].id=ma[name]; //存说话者 sen[i].s=a; //存说话内容 } for(int i=1;i<=m;++i) //假设第i个人是罪犯 for(int j=0;j<7;++j) //假设今天是S[j]天 judge(i,S[j]); if(!ans) //找不到罪犯 puts("Impossible"); else cout<<s[ans]; //罪犯名字 return 0; }