[BZOJ2523][Ctsc2001]聪明的学生
[BZOJ2523][Ctsc2001]聪明的学生
试题描述
一位教授逻辑学的教授有三名非常善于推理且精于心算的学生A,B和C。有一天,教授给他们三人出了一道题:教授在每个人脑门上贴了一张纸条并告诉他们,每个人的纸条上都写了一个正整数,且某两个数的和等于第三个。于是,每个学生都能看见贴在另外两个同学头上的整数,但却看不见自己的数。
这时,教授先对学生A发问了:“你能猜出自己的数吗?”A回答:“不能。”
教授又转身问学生B:“你能猜出自己的数吗?”B想了想,也回答:“不能。”
教授再问学生C同样的问题,C思考了片刻后,摇了摇头:“不能”。
接着,教授又重新问A同样的问题,再问B和C,……,经过若干轮的提问之后,当教授再次询问某人时,此人突然露出了得意的笑容,把贴在自己头上的那个数准确无误的报了出来。
现在,如果告诉你:教授在第N次提问时,轮到回答问题的那个人猜出了贴在自己头上的数是M,你能推断出另外两个学生的头上贴的是什么数吗?
[提示]
在没有人猜出自己头上的数之前,大家对教授提问的回答始终都是“不能”;而且除此之外在A,B,C之间是没有进行任何信息交流的。也就是说,每个人推断的依据仅仅是另外两个人的头上数,以及大家对教授的提问所做出的否定回答。
教授总是从学生A开始提问的。
你可以假定,这三个足够聪明的学生能够根据已知的条件在最早的轮次猜出自己的数,并且永远都不会猜错。
稍经分析和推理,你将得出以下结论:总是头上贴着最大的那个数的人最先猜出自己头上的数。
输入
包括若干组测试数据,其中的每一行代表一组测试数据,由两个整数N和M组成(即在教授第N次提问时,轮到回答问题的那个人猜出了贴在自己头上的数是M)。两个数之间用空格分隔开。最后,由-1 -1组成的一行标志着输入数据的结束。
0<N<500; 0<M<30000
输出
按照输入文件中的顺序依次给出各组数据的结果。
文件中对应每组数据的输出的第一行是一个整数p,是可能情况的总数。接下来的p行,每一行包括三个数,分别为贴在A,B,C头上的三个数。输出时,所有解按照A头上的数增序排列;在A头上的数相同的情况下,按照B头上的数增序排列。
输入示例
5 8 3 2 2 3 -1 -1
输出示例
3 2 8 6 5 8 3 6 8 2 1 1 1 2 1 2 3 1
数据规模及约定
见“输入”
题解
我是因为喜欢这题题号才做这题的。。。
整个题面有用的话只有最后一句:“总是头上贴着最大的那个数的人最先猜出自己头上的数。”
那么对于一个局面 (x, y, x+y),不妨设 x > y,那么 x+y 那个人能猜出自己脑袋上的数当且仅当他能排除自己脑袋上的数是 x-y 的可能性。怎么排除?就是假设自己脑袋上是 x-y,那么由于他知道另外两个人的数是啥(即 x 和 y),他也知道 x > y,那么就是说 (x, y, x-y) 中最大的是 x,那么如果在轮到头上是 x 的那个人在他理应猜出来的那步时还回答不出来,就说明自己头上的数不可能是 x-y,那么就可以从 (x, y, x-y) 这个局面的答案退出 (x, y, x+y) 这个局面的答案了。
最后我们暴力枚举可能的初始状态然后用迭代代替递归,就 rank 1 了。
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <cctype> #include <algorithm> using namespace std; int read() { int x = 0, f = 1; char c = getchar(); while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); } while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); } return x * f; } #define maxn 30010 int nxt[4] = {0, 2, 3, 1}, pre[4] = {0, 3, 1, 2}; struct Tri { int a, b, c; Tri() {} Tri(int _1, int _2, int _3): a(_1), b(_2), c(_3) {} bool operator < (const Tri& t) const { if(a != t.a) return a < t.a; if(b != t.b) return b < t.b; return c < t.c; } } ans[maxn]; bool check(int x, int y, int p, int n) { while(n > 0) { if(x == y) return n == p; if(x > y) { swap(x, y); y -= x; n -= 2; p = nxt[p]; } else { swap(x, y); x -= y; n--; p = pre[p]; } int a[4]; a[p] = x + y; a[nxt[p]] = x; a[pre[p]] = y; } return 0; } int main() { while(1) { int n = read(), s = read(); if(n == -1) break; int p = n % 3 ? n % 3 : 3, cnt = 0; for(int i = 1; i < s; i++) if(check(i, s - i, p, n)) { int a[4]; a[p] = s; a[nxt[p]] = i; a[pre[p]] = s - i; ans[++cnt] = Tri(a[1], a[2], a[3]); } sort(ans + 1, ans + cnt + 1); printf("%d\n", cnt); for(int i = 1; i <= cnt; i++) printf("%d %d %d\n", ans[i].a, ans[i].b, ans[i].c); } return 0; }