C语言每日一练——第74天:黑与白问题
C语言每日一练
2022年1月10日
文章目录
题目描述
有A、B、C、D、E这5个人,每个人额头上都帖了一张黑或白的纸。5人对坐,每个人都可以看到其他人额头上纸的颜色。5人相互观察后:
A说:“我看见有3人额头上贴的是白纸,1人额头上贴的是黑纸。”
B说:“我看见其他4人额头上贴的都是黑纸。”
C说:“我看见1人额头上贴的是白纸,其他3人额头上贴的是黑纸 。”
D说:“我看见4人额头上贴的都是白纸。”
E什么也没说。
现在己知额头上贴黑纸的人说的都是谎话,额头贴白纸的人说的都是实话。问这5人谁的额头上贴的是白纸,谁的额头上贴的是黑纸?
问题分析
个人思路:<仅供参考>
总共5个人,说话的只有4个,简单分析我们可以得知:
4个人说的情况都不相同。A说看到3个人贴白纸,B看到0张白纸,C看到1张白纸,D看到4张白纸,假设他们说的都是真话,那么他们4人所说的话对应的总白纸数依然不同(因为他们自己额头上贴的都是白纸,并不会使其中两人白纸总数相同)。
//4人贴白纸时,总白纸数 #define A_TRUE (3 + 1) //其他3人贴白纸,自己也贴白纸 #define B_TRUE (0 + 1) #define C_TRUE (1 + 1) #define D_TRUE (4 + 1)
所以我们可以得出一个隐藏条件——说话的4个人中只有1个人额头上贴白纸,即只有一个人说的是真话。
利用上面这个条件,我们可以通过所有人额头上的总白纸数来判断哪个人说的是真话,例如:B额头贴白纸,他说其他4人贴黑纸,在遍历所有情况时,如果总白纸数为1,且只有B贴白纸时,可能就是正确情况。
为什么说可能呢?由于每个人说的话中没有考虑自己头上的纸张颜色,所以可能会一种矛盾现象:
B额头上贴了白纸,他看到其他4人都贴的是黑纸,C虽然头上贴的的确是黑纸,但他的话是正确的:“我看见1人额头上贴的是白纸,其他3人额头上贴的是黑纸 ”。这就和题目条件冲突了——额头上贴黑纸的人说的都是谎话。
这样一来,在遍历时还需考虑当1个人贴白纸时,其他4个人都不能说真话这一条件,这一点依然可以通过总白纸数来判断。例如:当遍历到B说真话(额头贴白纸)时,判断5个人总白纸数是否等于A,C,D 3个人看到的白纸数(见下面的宏定义),如果有一个相等,说明该遍历结果是错误的。
//4人贴黑纸时,总白纸数(假设它们仍然说真话) #define A_FALSE (3) //自己贴黑纸,但说的是真话 #define B_FALSE (0) #define C_FALSE (1) #define D_FALSE (4)
程序实现上,定义
a,b,c,d,e5个变量,分别表示他们额头上贴的纸张颜色,1表示白色。通过5层
for循环,遍历所以可能性,同时满足上面两种情况的,即为正确结果。
【注】我的思路可能推理得太深了,如果觉得处理得太麻烦,可以直接看下文网上参考的思路,该思路逻辑性更强,代码量更少。
代码实现
#include <stdio.h> //4人贴白纸时,总白纸数 #define A_TRUE (3 + 1) //其他3人贴白纸,自己也贴白纸 #define B_TRUE (0 + 1) #define C_TRUE (1 + 1) #define D_TRUE (4 + 1) //4人贴黑纸时,总白纸数(假设它们仍然说真话) #define A_FALSE (3) //自己贴黑纸,但说的是真话 #define B_FALSE (0) #define C_FALSE (1) #define D_FALSE (4) int main() { int A = 0, B = 0, C = 0, D = 0, E = 0; int tmp = 0, flag = 0; //flag = 1表示找到正确结果 for(A = 0; A <= 1; A++) for(B = 0; B <= 1; B++) for(C = 0; C <= 1; C++) for(D = 0; D <= 1; D++) for(E = 0; E <= 1; E++) { //获取贴白纸的人数 tmp = A + B + C + D + E; flag = 0; switch(tmp) { //4人只有A贴白纸,且贴黑纸的人说的话不能为真 case A_TRUE: if(A && !B && !C && !D && tmp != B_FALSE && tmp != C_FALSE && tmp != D_FALSE) flag = 1; break; //4人只有B贴白纸,且贴黑纸的人说的话不能为真 case B_TRUE: if(B && !A && !C && !D && tmp != A_FALSE && tmp != C_FALSE && tmp != D_FALSE) flag = 1; break; //4人只有C贴白纸,且贴黑纸的人说的话不能为真 case C_TRUE: if(C && !A && !B && !D && tmp != A_FALSE && tmp != B_FALSE && tmp != D_FALSE) flag = 1; break; //4人只有D贴白纸,且贴黑纸的人说的话不能为真 case D_TRUE: if(D && !A && !B && !C && tmp != A_FALSE && tmp != B_FALSE && tmp != C_FALSE) flag = 1; break; } if(flag) { printf("A贴%s\n", A ? "白纸" : "黑纸"); printf("B贴%s\n", B ? "白纸" : "黑纸"); printf("C贴%s\n", C ? "白纸" : "黑纸"); printf("D贴%s\n", D ? "白纸" : "黑纸"); printf("E贴%s\n", E ? "白纸" : "黑纸"); } } return 0; }
运行结果
后期完善
既然然已经推理出了A、B、C、D 4个人中只有一个人额头贴白纸,这时只需用一个
for循环就能遍历出4个人中谁贴了白纸,循环次数为4次(0~3),分别表示A、B、C、D,这样可以减少一些无效的循环次数。
#include <stdio.h> //4人贴白纸时,总白纸数 #define A_TRUE (3 + 1) //其他3人贴白纸,自己也贴白纸 #define B_TRUE (0 + 1) #define C_TRUE (1 + 1) #define D_TRUE (4 + 1) //4人贴黑纸时,总白纸数(假设它们仍然说真话) #define A_FALSE (3) //自己贴黑纸,但说的是真话 #define B_FALSE (0) #define C_FALSE (1) #define D_FALSE (4) int main() { //i表示A,B,C,D 4个人哪个贴白纸,j表示E是否贴白纸 int i = 0, j = 0; int tmp = 0, flag = 0; //flag = 1表示找到正确结果 for(i = 0; i < 4; i++) for(j = 0; j <= 1; j++) { flag = 0; tmp = 1 + j; //总白纸数 switch(i) { //A说的是真话,其他人说假话? case 0: if(tmp == A_TRUE && tmp != B_FALSE && tmp != C_FALSE && tmp != D_FALSE) flag = 1; break; //B说的是真话,其他人说假话? case 1: if(tmp == B_TRUE && tmp != A_FALSE && tmp != C_FALSE && tmp != D_FALSE) flag = 1; break; //C说的是真话,其他人说假话? case 2: if(tmp == C_TRUE && tmp != A_FALSE && tmp != B_FALSE && tmp != D_FALSE) flag = 1; break; //D说的是真话,其他人说假话? case 3: if(tmp == D_TRUE && tmp != A_FALSE && tmp != B_FALSE && tmp != C_FALSE) flag = 1; break; } if(flag) { printf("A贴%s\n", i == 0 ? "白纸" : "黑纸"); printf("B贴%s\n", i == 1 ? "白纸" : "黑纸"); printf("C贴%s\n", i == 2 ? "白纸" : "黑纸"); printf("D贴%s\n", i == 3 ? "白纸" : "黑纸"); printf("E贴%s\n", j == 1 ? "白纸" : "黑纸"); } } return 0; }
网上参考
原文链接:http://c.biancheng.net/cpp/html/3357.html
原文部分思路:
我的思路有一个很大的缺点,那就是花太多心思去推理了(推理过深),好处就是理解起来会好一点。这份代码通过逻辑表达式的方法,一个
if语句就得出了结果,优点是逻辑性强,代码量少,不过理解起来可能需要一定的逻辑思维能力
#include<stdio.h> int main() { int a, b, c, d, e; /*0表示黑色,1表示白色*/ for(a=0; a<=1; a++) /*穷举五个人额头帖纸颜色的全部可能*/ for(b=0; b<=1; b++) for(c=0; c<=1; c++) for(d=0; d<=1; d++) for(e=0; e<=1; e++) if( (a&&b+c+d+e==3 || !a&&b+c+d+e!=3) && (b&&a+c+d+e==0 || !b&&a+c+d+e!=0) && (c&&a+b+d+e==1 || !c&&a+b+d+e!=1) && (d&&a+b+c+e==4 || !d&&a+b+c+e!=4) ) { printf("A额头上的贴纸是%s色的.\n",a?"白":"黑"); printf("B额头上的贴纸是%s色的.\n",b?"白":"黑"); printf("C额头上的贴纸是%s色的.\n",c?"白":"黑"); printf("D额头上的贴纸是%s色的.\n",d?"白":"黑"); printf("E额头上的贴纸是%s色的.\n",e?"白":"黑"); } return 0; }
- C语言每日小练(三)——竖式问题
- C语言黑与白问题代码及解析(内附视频)
- C语言每日一练——第70天:24点问题
- 每日总结关于c语言中不会问题的解答
- 每日一点C语言--开灯问题
- C语言每日小练(一)——n!问题
- 『每日一题 2012-02-10』猴子选大王问题 C语言实现
- C语言每日小练(三)——abc排列问题
- C语言每日小练(一)——7744问题
- C语言每日小练(二)——开灯问题
- C语言每日小练(一)——3n+1问题
- (一)C语言汉诺塔问题(小白的每日总结)
- 【C语言】scanf返回值问题
- C语言使用深度优先搜索算法解决迷宫问题(堆栈)
- 关于C语言中的结构体对齐问题
- c语言全局变量和局部变量问题汇总
- c语言的printf一个小问题
- C语言的指针大小问题
- C语言——0-1背包问题
- C语言巧用switch..case..,if ...else ..if..语句执行效率问题: