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");
}
}