您的位置:首页 > 其它

bzoj 2523: [Ctsc2001]聪明的学生 记忆化搜索

2016-04-08 09:19 218 查看
我个人认为题面中最后一句话的提示才是关键。。。然而窝智商不足推不出来,(更不要说提示中的稍加推理了)

先看一下怎么样可以推出这三个数字,对于一个人,加入它看到的是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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: