C语言每日一练——第74天:黑与白问题

Posted 小辉_Super

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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,e 5个变量,分别表示他们额头上贴的纸张颜色,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语言每日一练——第74天:黑与白问题的主要内容,如果未能解决你的问题,请参考以下文章

C语言每日一练——第126天:佩奇借书问题

C语言每日一练——第147天:兔子产子问题

C语言每日一练——第154天:牛顿迭代法求方程根

C语言每日一练——第70天:24点问题

C语言每日一练——第64天:自动发牌程序

C语言每日一练——第90天:青蛙跳台阶(升级版)