题目描述
明明同学最近迷上了侦探漫画《柯南》并沉醉于推理游戏之中,于是他召集了一群同学玩推理游戏。游戏的内容是这样的,明明的同学们先商量好由其中的一个人充当罪犯(在明明不知情的情况下),明明的任务就是找出这个罪犯。接着,明明逐个询问每一个同学,被询问者可能会说:
证词中出现的其他话,都不列入逻辑推理的内容。
明明所知道的是,他的同学中有NN个人始终说假话,其余的人始终说真。
现在,明明需要你帮助他从他同学的话中推断出谁是真正的凶手,请记住,凶手只有一个!
输入格式
输入由若干行组成,第一行有三个整数,M(1≤M≤20)M(1≤M≤20)、N(1≤N≤M)N(1≤N≤M)和P(1≤P≤100)P(1≤P≤100);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
说明/提示
【题目来源】
NOIP 2003 提高组第二题
上代码:
#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]=='\n'||a[a.length()-1]=='\r') //把坑爹的换行符搞掉
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;
}