我个人认为题面中最后一句话的提示才是关键。。。然而窝智商不足推不出来,(更不要说提示中的稍加推理了)
先看一下怎么样可以推出这三个数字,对于一个人,加入它看到的是x,y,那么显然他头上的数只可能是x+y或者|x-y|,那么他能得到结论当且仅当通过观察或者推理排除了一种情况,那么他头上的数字只可能是另一种了。那么现在根据提示,显然最先得出结论的那个人头上的数字只可能是x+y,也就是他排除了|x-y|的可能;那么他是怎么排除的呢?,显然他可以假设如果自己头上的是|x-y|,那么他显然不是最大的,那么最大的那个人肯定在某一轮就推理出来了;那么如果到了某一轮还没有人推理出来,那么下一次到他的时候他就能知道自己是x+y了。
因此如果给定一个三元组类似于(x,x+y,y),不妨设x>y,那么递归调用可以得到(x,x-y,y)的最小猜测次数,那么如果在这个次数还没有猜出来的话,显然中间那个人就知道自己是x+y了,因此有(x,x+y,y)=(x,x-y,y)+1;其余情况同理可得。
因此我们可以直接分六种情况讨论;三种情况结束直接暴力记忆化搜索。
注意可以换一种表示用(x,y,t)表示最大的在第t位,他后面一个是x,再后面一个是y,这样转移就只有两种情况了;结束只有一种情况。然后用迭代代替递归。这样就轻松rank1辣~~~\(≧▽≦)/~~~(比rank2快整整200ms~~~)
AC代码如下:
#include<iostream> #include<cstdio> #include<cstring> using namespace std; const int pre[3]={2,0,1},nxt[3]={1,2,0}; int n,m,ans[30005][3]; bool ok(int x,int y,int t){ int cnt=n; while (cnt>0) if (x==y) return cnt==t+1; else if (x>y){ y=x-y; x-=y; cnt-=2; t=nxt[t]; } else{ x=y-x; y-=x; cnt--; t=pre[t]; } return 0; } int main(){ for (scanf("%d%d",&n,&m); n!=-1 && m!=-1; scanf("%d%d",&n,&m)){ int i,t1=n%3,t0=pre[t1],t2=nxt[t1],cnt=0; for (i=1; i<m; i++) if (ok(i,m-i,t0)){ ans[++cnt][t0]=m; ans[cnt][t1]=i; ans[cnt][t2]=m-i; } printf("%d\n",cnt); if (t0==1) for (i=cnt; i; i--) printf("%d %d %d\n",ans[i][0],ans[i][1],ans[i][2]); else for (i=1; i<=cnt; i++) printf("%d %d %d\n",ans[i][0],ans[i][1],ans[i][2]); } return 0; }
by lych
2016.4.8