题目描述
明明同学最近迷上了侦探漫画《柯南》并沉醉于推理游戏之中,于是他召集了一群同学玩推理游戏。游戏的内容是这样的,明明的同学们先商量好由其中的一个人充当罪犯(在明明不知情的情况下),明明的任务就是找出这个罪犯。接着,明明逐个询问每一个同学,被询问者可能会说:
证词中出现的其他话,都不列入逻辑推理的内容。
明明所知道的是,他的同学中有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??
输出
MIKE
解答
这题就是个坑←_←,这数据就是个逗比→_→
用记录i对j是不是凶手的判断:
时,i认为j是凶手;
时,i认为j不是凶手;
记录第i个人的名字
用记录第i个人认为今天是星期几
然后咱枚举犯人(guilty)和星期几(today)来对每个人逐一判断(不会傻到用组合去判断谁说假话吧0.0):
咱们用f数组记录我们对第i个人的判断
如果说明他说真话,说明他说假话(注意清0)
由于全部人分为三类:只说真话,只说假话,说废话
所以如果有人既说过真话又说过假话这种情况是不会存在的,直接判断出false就行
然后分类讨论:
1、
说明i说真话,如果即对于这组guilty、today他说过假话,矛盾,返回false 不然的话他说真话
2、
说明i说假话,如果即他说过真话,矛盾返回false,不然
3、对于i对除了guilty以外所有人(j<>guilty)的判断
(1)如果说明他认为j是凶手,是假话,如果即他说过真话,矛盾返回false,不然
(2)如果说明他认为j不是凶手,是真话,如果即他说过假话,矛盾返回false 不然的
4、a[i].day>0即他说过今天是星期几并且a[i].j<>today 即他说错了,是假话,如果即他说过真话,矛盾返回false,不然
判断完毕=w=
然后我们统计即说谎的人数t1和即不确定的人数t2
然后关键来了
究竟什么是满足要求的情况呢?
t1不一定非要严格等于说谎人数m,
因为有人不确定,
而这批不确定的人中可能也有人是说谎者只是他没说有用的而已(这里一开始没想到跪了3个小时QAQ)
【我走的最长的路就是你的套路】
所以满足的条件是 ()and ()
由于结果有三种情况:
1、真相只有一个——有且只有1个满足条件的犯人,直接输出名字
2、他们同伙作案——有大于1个人满足他是犯人的条件,输出Cannot Determine
3、错误的嫌疑人——没有人满足犯人的条件,输出 Impossible
所以我们如果判断出一个人是犯人不要着急输出,
而是记录我们找到的满足犯人条件的人数t,以及他的名字ans,
每找到一个就t+1,ans更新为他的名字
一个小细节就是我们是犯人和星期同时枚举的,
所以如果我们判断出一个人是犯人就可以直接去判断下一个人不然这一个人会加好几遍
还有判断的时候是区分大小写的,
第一组数据中叫GUILTY的那个人说:I am GUILTY 真的是句废话,他就是说他叫GUILTY(=。=)
AC代码:
用记录i对j是不是凶手的判断:
时,i认为j是凶手;
时,i认为j不是凶手;
记录第i个人的名字
用记录第i个人认为今天是星期几
然后咱枚举犯人(guilty)和星期几(today)来对每个人逐一判断(不会傻到用组合去判断谁说假话吧0.0):
咱们用f数组记录我们对第i个人的判断
如果说明他说真话,说明他说假话(注意清0)
由于全部人分为三类:只说真话,只说假话,说废话
所以如果有人既说过真话又说过假话这种情况是不会存在的,直接判断出false就行
然后分类讨论:
1、
说明i说真话,如果即对于这组guilty、today他说过假话,矛盾,返回false 不然的话他说真话
2、
说明i说假话,如果即他说过真话,矛盾返回false,不然
3、对于i对除了guilty以外所有人(j<>guilty)的判断
(1)如果说明他认为j是凶手,是假话,如果即他说过真话,矛盾返回false,不然
(2)如果说明他认为j不是凶手,是真话,如果即他说过假话,矛盾返回false 不然的
4、a[i].day>0即他说过今天是星期几并且a[i].j<>today 即他说错了,是假话,如果即他说过真话,矛盾返回false,不然
判断完毕=w=
然后我们统计即说谎的人数t1和即不确定的人数t2
然后关键来了
究竟什么是满足要求的情况呢?
t1不一定非要严格等于说谎人数m,
因为有人不确定,
而这批不确定的人中可能也有人是说谎者只是他没说有用的而已(这里一开始没想到跪了3个小时QAQ)
【我走的最长的路就是你的套路】
所以满足的条件是 ()and ()
由于结果有三种情况:
1、真相只有一个——有且只有1个满足条件的犯人,直接输出名字
2、他们同伙作案——有大于1个人满足他是犯人的条件,输出Cannot Determine
3、错误的嫌疑人——没有人满足犯人的条件,输出 Impossible
所以我们如果判断出一个人是犯人不要着急输出,
而是记录我们找到的满足犯人条件的人数t,以及他的名字ans,
每找到一个就t+1,ans更新为他的名字
一个小细节就是我们是犯人和星期同时枚举的,
所以如果我们判断出一个人是犯人就可以直接去判断下一个人不然这一个人会加好几遍
还有判断的时候是区分大小写的,
第一组数据中叫GUILTY的那个人说:I am GUILTY 真的是句废话,他就是说他叫GUILTY(=。=)
AC代码:
#include<cstdio> #include<map> #include<iostream> #include<string> using namespace std; int n,m,p,s[22][102],l[22],d[22][102],day[22][102],gu[22]; map<string,int>t; map<int,string>h; string u,v[102],g[102],q[102],bb; int main() { scanf("%d%d%d",&m,&n,&p); for(int i=0;i<m;i++) { cin>>u; t[u]=i+1; h[i+1]=u; } for (int i=0;i<p;i++) { int y,ll=1;u=""; while(u[ll-1]!='.'&&u[ll-1]!='?'&&u[ll-1]!='!') { cin>>bb; if(ll>1)u+=' '; u+=bb; ll=u.length(); } for(int j=0;j<ll&&u[j]!=':';j++) { v[i]+=u[j]; y=j; } int jj=t[v[i]],b=-1,nn=0,uu=0; for(int j=y+3;j<ll;j++) { if(!uu) g[i]+=u[j];else q[i]+=u[j]; 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; } 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; 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; } } for(int i=1;i<=m;i++) for(int j=1;j<=7;j++) { int fa=0,T,F,abc=0; for(int k=1;k<=m;k++) { T=F=0; 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; if(fa==n||fa<=n&&fa+abc>=n)gu[i]=1; } 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; }
来源:心之所向 素履以往