bzoj2523: [Ctsc2001]聪明的学生

8 篇文章 0 订阅

原谅我逻辑能力差

首先看到题目的提示:稍经分析和推理,你将得出以下结论:总是头上贴着最大的那个数的人最先猜出自己头上的数。

然而怎么证?!

对于A,如果B,C数字相同,他可以立刻得知它头上的是B+C。否则有两种可能:B+C或|B-C|。

不妨设A>B>C,A=B+C,经过n次询问,A可以猜出。那么对于A,A+C,C,经过n+1次询问,B认为如果他是A-C,A应当在n次询问时得出答案,因此他不是|A-C|,他可以得出他是最大的。

假设刚才的证明没有问题,设A>B>C,A假设他自己是|B-C|,那么上一轮C不是最大,无法推断,在上一轮B最大,他会假设他是|A-C|,那么上一轮A……直到某个人看到另外2人一样。因为询问从A开始所以还要倒推至A,然后你会发现这是正确的。这样我们可以在O(n)时间内判断某组数字能否在正好n次询问时解决

已知n次询问,最大的是m,O(nm)枚举每一种情况,得到答案。

(work()中的x是不必要的,可以删除)

#include<iostream>
#include<cstdio>
#include<cstdlib>
using namespace std;
int n,m,k,cnt,Out[3][10000],Ans,a,b,c;
int work(int x,int y,int z,int t,int sum)
{
	if (sum>n) return 100;
	if (t==0) t=3;
	if (y==z) return t;
	return work(y,z,abs(y-z),t-1,sum+1)+1;
}
void doit(int x,int y,int z)
{
	if (k==1) Ans=work(x,z,y,1,0);
	else if (k==2) Ans=work(x,y,z,2,0);
	else Ans=work(x,z,y,3,0);
	if (Ans==n) 
	{
		cnt++;Out[a][cnt]=x;Out[b][cnt]=y;Out[c][cnt]=z;
	}
}
int main()
{
	while(scanf("%d%d",&n,&m)&&m!=-1)
	{
		k=n%3;cnt=0;
		if (k==1) a=0,b=1,c=2;
		else if (k==2) a=1,b=0,c=2;
		else a=2,b=0,c=1;
		for (int i=1;i<m;i++)
			doit(m,i,m-i);
		printf("%d\n",cnt);
		for (int i=1;i<=cnt;i++) printf("%d %d %d\n",Out[0][i],Out[1][i],Out[2][i]);
	}
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值