逻辑推理在生活中处处可见,它有助于我们锻炼自己的思维
这里有这样一道很经典的逻辑推理题,看似简单里面却暗藏很多陷阱,往往大多数人都得到的是不完全正确的答案;
让我们来分析一下这道典例
5位运动员参加了10米台跳水比赛,有人让他们预测比赛结果
A选手说:B第二,我第三;
B选手说:我第二,E第四;
C选手说:我第一,D第二;
D选手说:C最后,我第三;
E选手说:我第四,A第一;
比赛结束后,每位选手都说对了一半,请编程确定比赛的名次
解题思路:
1.看到这种题,我们首先需要把所有结果都陈列出来,然后对每一次结果进行判断,然后对不符合条件的排名情况舍弃,最终就能得到我们的正确名次;
2.对于每位选手只说对了一半我们该如何来判断呢?
例如对于A选手,我们如何判断他说对了一半?
有人会立马想到:if ( b == 2 || a== 3)
这无疑是有漏洞的:b==2和a==3都成立这条件也为真,然而题目只需要有且只能说对一半;
然后会有人想到:if ( (b == 2 || a== 3) == 1 && (b == 2 && a== 3)==0 )
对,这能满足题目的需求,达到只能说对一半的要求,但这样写是否过于复杂了?
下面介绍一种最优的解法:
if((b == 2) + (a == 3) == 1);
3.在做这道题时,我们得先了解一个常识,这也是本题的关键和陷阱所在。在排名中我们来列举如下情况:
1. a、b、c、d、e的名次为 1,2,3,4,5;
2. a、b、c、d、e的名次为 1,1,3,4,5;
3. a、b、c、d、e的名次为 1,1,2,3,2;
4. a、b、c、d、e的名次为 1,3,3,4,5;
其中哪些排名是不可能的呢?
2和4的排名是不可能出现的。生活实际中,排名是可以重叠的(两个或多个并列一个名次),但不能跨级,意思是,有第一名,没第二名,却有第三名,这样的情况你在生活实际中遇到过吗?肯定是不可能的。
所以,我们在判断排名是否合格时要排除跨级的这种情况。
那么难点来了,如何判断这五个人的排名是否跨级了?
解法:我们将这五个人的名次都放到 flag 变量(初始化为零)的比特位中,如果出现第一名,就让第一个比特位(从右往左)置一,如果出现了第二名,就让第二个比特位置一,....... 然后我们来判断flag从右往左如果比特位之间有零,则不符合要求(即比特位从右往左不能出现1 0 1 这种序列)
下面呈上源码:
#include<stdio.h>
/*5位运动员参加了10米台跳水比赛,有人让他们预测比赛结果
A选手说:B第二,我第三;
B选手说:我第二,E第四;
C选手说:我第一,D第二;
D选手说:C最后,我第三;
E选手说:我第四,A第一;
比赛结束后,每位选手都说对了一半,请编程确定比赛的名次。 */
//注意:续航符打出后需直接回车,然后再写代码,回车前回车会不能有空格,否则就会有警告
//注意:续航符后面不能加注释,否则直接报错
int main()
{
int a,b,c,d,e;
int flag = 0;
for (a = 1; a <= 5; a++)
for (b = 1; b <= 5; b++)
for (c = 1; c <= 5; c++)
for (d = 1; d <= 5; d++)
for (e = 1; e <= 5; e++)
{
flag = 0;
if ((b == 2) + (a == 3) == 1 && \
(b == 2) + (e == 4) == 1 && \
(c == 1) + (d == 2) == 1 && \
(c == 5) + (d == 3) == 1 && \
(e == 4) + (a == 1) == 1)
{
flag |= 1 << (a-1);
flag |= 1 << (b-1);
flag |= 1 << (c-1);
flag |= 1 << (d-1);
flag |= 1 << (e-1);
while (flag)
{
if (!(flag&1))
break;
flag >>= 1;
}
if (flag == 0)
{
printf("a:%d b:%d c:%d d:%d e:%d\n", a, b, c, d, e);
}
}
}
return 0;
}