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

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

       先看一下怎么样可以推出这三个数字,对于一个人,加入它看到的是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

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值