[BZOJ 2523][Ctsc2001]聪明的学生(神奇的思路题)
Description
一位教授逻辑学的教授有三名非常善于推理且精于心算的学生A,B和C。有一天,教授给他们三人出了一道题:教授在每个人脑门上贴了一张纸条并告诉他们,每个人的纸条上都写了一个正整数,且某两个数的和等于第三个。于是,每个学生都能看见贴在另外两个同学头上的整数,但却看不见自己的数。
这时,教授先对学生A发问了:“你能猜出自己的数吗?”A回答:“不能。”
教授又转身问学生B:“你能猜出自己的数吗?”B想了想,也回答:“不能。”
教授再问学生C同样的问题,C思考了片刻后,摇了摇头:“不能”。
接着,教授又重新问A同样的问题,再问B和C,……,经过若干轮的提问之后,当教授再次询问某人时,此人突然露出了得意的笑容,把贴在自己头上的那个数准确无误的报了出来。
现在,如果告诉你:教授在第N次提问时,轮到回答问题的那个人猜出了贴在自己头上的数是M,你能推断出另外两个学生的头上贴的是什么数吗?
[提示]
在没有人猜出自己头上的数之前,大家对教授提问的回答始终都是“不能”;而且除此之外在A,B,C之间是没有进行任何信息交流的。也就是说,每个人推断的依据仅仅是另外两个人的头上数,以及大家对教授的提问所做出的否定回答。
教授总是从学生A开始提问的。
你可以假定,这三个足够聪明的学生能够根据已知的条件在最早的轮次猜出自己的数,并且永远都不会猜错。
稍经分析和推理,你将得出以下结论:总是头上贴着最大的那个数的人最先猜出自己头上的数。
Solution
神奇的猜数问题
我们首先需要知道最大的数是怎么被猜出来的
1.当一个人看到另两个人的数字一样时,肯定可以推断自己头上的数是他们的和
2.一个人看到另两个人头上的数分别为x,y 则自己只可能是x+y或|x-y|;某种情况下,假设自己头上是|x-y|其他的人就已经可以推断出来自己的数,则说明自己不可能是|x-y|,所以猜到自己头上是x+y
已经知道了最大的数,于是说就枚举另两个数的可能,并判断猜的次数是不是n
work(int x,int y,int t) 表示在位置t的人看到下一个人是x,再下一个人是y,递归计算需要猜多少次
#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> using namespace std; int n,m,pre[3]={2,0,1},next[3]={1,2,0},ans[30005][3],Ans,cnt; void work(int x,int y,int t) { if(Ans>n)return; if(x==y){Ans+=t+1;return;} if(x>y){Ans+=2;work(y,x-y,next[t]);} else{Ans++;work(y-x,x,pre[t]);} } int main() { while(~scanf("%d%d",&n,&m)&&n!=-1) { cnt=0; for(int i=1;i<m;i++) { int j=m-i; Ans=0; work(i,j,(n-1)%3); if(Ans==n) ans[++cnt][next[(n-1)%3]]=i,ans[cnt][(n-1)%3]=m,ans[cnt][pre[(n-1)%3]]=j; } printf("%d\n",cnt); if((n-1)%3==1) for(int i=cnt;i>0;i--) printf("%d %d %d\n",ans[i][0],ans[i][1],ans[i][2]); else for(int i=1;i<=cnt;i++) printf("%d %d %d\n",ans[i][0],ans[i][1],ans[i][2]); } return 0; }
看到lych的题解发现其实是可以用迭代代替递归的0 0
本想刷一下Ranklist的,结果没有快很多QvQ
bool work(int x,int y,int t) { for(;;) { if(x==y)return Ans==t+1; else if(x>y)Ans-=2,y=x-y,x-=y,t=next[t]; else Ans--,x=y-x,y-=x,t=pre[t]; } }