CSTC 2001 聪明的学生 BZOJ 2523 递归(类搜索,推理)

17 篇文章 0 订阅
12 篇文章 0 订阅

今天我们来看一道比较奇怪的题 这是一道递归的类搜索题 不过思维性特别强
下面是题干 哦 对了 还是一道游戏题

一位教授逻辑学的教授有三名非常善于推理且精于心算的学生A,B和C。有一天,教授给他们三人出了一道题:教授在每个人脑门上贴了一张纸条并告诉他们,每个人的纸条上都写了一个正整数,且某两个数的和等于第三个。于是,每个学生都能看见贴在另外两个同学头上的整数,但却看不见自己的数。

这时,教授先对学生A发问了:“你能猜出自己的数吗?”A回答:“不能。”
教授又转身问学生B:“你能猜出自己的数吗?”B想了想,也回答:“不能。”
教授再问学生C同样的问题,C思考了片刻后,摇了摇头:“不能”。
接着,教授又重新问A同样的问题,再问B和C,……,经过若干轮的提问之后,当教授再次询问某人时,此人突然露出了得意的笑容,把贴在自己头上的那个数准确无误的报了出来。

现在,如果告诉你:教授在第N次提问时,轮到回答问题的那个人猜出了贴在自己头上的数是M,你能推断出另外两个学生的头上贴的是什么数吗?

[提示]
在没有人猜出自己头上的数之前,大家对教授提问的回答始终都是“不能”;而且除此之外在A,B,C之间是没有进行任何信息交流的。也就是说,每个人推断的依据仅仅是另外两个人的头上数,以及大家对教授的提问所做出的否定回答。
教授总是从学生A开始提问的。
你可以假定,这三个足够聪明的学生能够根据已知的条件在最早的轮次猜出自己的数,并且永远都不会猜错。
稍经分析和推理,你将得出以下结论:总是头上贴着最大的那个数的人最先猜出自己头上的数。

为了更好理解 我决定带大家实践一次

我们先假设
A的头上是 2
B的头上是 8
C的头上是 6
第一轮 问道ABC 肯定没什么说的 猜不出来
再看第二轮 A 没有什么说的 因为他不能分析到有效的信息
但是 B 就不同了

    首先
    B知道 他看到的是  A 2   C  6
    那么没什么说的  自己只能是 48
    但是如果自己是 4  那么
    #C其实一开始就能猜出自己的编号
        原因是这样的
            因为 C看到的会是我的 4 和A 的2
            但是如果 C就面临着  62 的选择
            但是如果 C2
            其实我就能猜出来 
                因为C2
                A是2
                2-2==0
                所以不可能 只能是  2+2=4
                这样的话也就是说 C不可能是2
                C只能是 6 
                所以他无法推出
            同时
            我只能是 8!!!

没错 如果是 2 8 6 的情况 B就是这么推导出自己的数字 也就是 第5轮就得出了答案

  1. 更朴素一点的呢?
    由原题中的结论 最大的总是能先猜出(方才我们也通过模拟验证了一次)
    所以说 X1 X2 X3 只要一定 轮数就其实已经一定了
    那我们不妨就让B是那个最大的
    那么B判断出自己数字的依据是
    X2!=|x1-x3|
    1. 继续 我们可以递归子问题 再次化为新的 x1 x2 x3 进行解决问题
    2. 递归的方式详见代码
    3. 之前的分析都是已知头上的数来算轮数 对于本题的转化 只需要进行简单的枚举i即可

下面是代码 还是 不太好看 自己看着办

#include <bits/stdc++.h>
using namespace std;
#define MAXN 10000+500
int n,m,num[2222][4];
int p(int x,int y)
{
    int c=y-x;
    if(c<0)
        c+=3;
    return c;
}
int times(int i,int j, int t1,int t2,int t3)
{
    int ans=0;
    if(j<=0||i<=0) return 0;
    if(i==j)
        ans+=t3;
    else if(i>j)
        ans+=times(j,i-j,t2,t3,t1)+p(t1,t3);
    else
        ans+=times(i,j-i,t1,t3,t2)+p(t2,t3);
    return ans;
}
int main()
{
    while(scanf("%d%d",&n,&m)&&n+m!=-2)
    {
        if(n==-1&&m==-1)
            return 0;
        int t=n%3,a,b;
        if(t==0)
            a=1,b=2,t=3;
        else if(t==2)
            a=1,b=3;
        else a=2,b=3;
        int tot=0;
        for(int i=1; i<m; i++)
        {
            if(times(i,m-i,a,b,t)==n)
            {
                num[tot][a]=i;
                num[tot][b]=m-i;
                num[tot++][t]=m;
            }
        }
        printf("%d\n",tot);
        for(int i=0;i<tot;i++)
                printf("%d %d %d\n",num[i][1],num[i][2],num[i][3]);
    }
    return 0;
}
还是有点草率.........就这样吧 不会的再说
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值