C语言应用题——如何确定跳水排名


问题描述


问题是这样的:

1


思考


首先,某人比赛的名次一定可能是1-5中的任意一个。所以得用5个for循环来实现

其次,这5个人的话都不是只关于自己,而且还有关于别人的。所以上边的5个for循环一定不能是各自独立的。他们得相互嵌套起来,形成约束

最后,每个人的话都只说对了一半,所以可以用if语句来判断。

这样,我们就可以写出代码了。


Code (v1.0)


#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<Windows.h>
int main()
{
    //a.b.c.d.e均可能是1-5名,为了方便表示其关联关系,形成for循环嵌套
    for (int a = 1; a <= 5; a++)
    {
        for (int b = 1; b <= 5; b++)
        {
            for (int c = 1; c <= 5; c++)
            {
                for (int d = 1; d <= 5; d++)
                {
                    for (int e = 1; e <= 5; e++)
                    {
                        if (((b == 1) + (a == 3) == 1)
                            && ((b == 2) + (e == 4) == 1)
                            && ((c == 1) + (d == 2) == 1)
                            && ((c == 5) + (d == 3) == 1)
                            && ((e == 4) + (a == 1) == 1))
                            //所有的条件都只有1个为真
                        {
                            printf("a= %d, b= %d, c= %d, d= %d, e=%d\n", a, b, c, d, e);
                        }

                    }
                }
            }
        }
    }
system("pause");
return 0;
}

运行代码:

2

很显然,这是不合理的。排名只可能有一种,而且应该是连续的数字,上边的结果有的没有第一名,有的中间缺少第二名。只有第一种结果看起来比较合理。

所以网上有人想出了这种解决办法:

            {
                        printf("a= %d, b= %d, c= %d, d= %d, e=%d\n", a, b, c, d, e);
                        system("pause");
                        return 0;
            }

即在printf之后直接返回,这样程序结果就变成:

3

结果居然出来了。

但是,这种办法是一种投机取巧的做法,在代码中加return 0,使其只输出第一个结果就返回,当然不可取

那么这个问题到底怎么解决呢?


Solution


人类的智慧是无穷的,此路不通,自会开辟新经。

下面博主给大家安利一种解法,当然,这不是我的原创。如果你有更好的办法,请在下方留言,或者私信我告诉你的做法。本人不胜荣幸。

俗话说,解铃还须系铃人。要想得到解决办法,必须要找到问题。

我们再来思考一下这个问题:

刚才程序运行出的结果有很多,但我们知道,正确的结果应该只有一个,所以问题的关键在于如何过滤掉其他的错误结果

其实这个问题还有两个隐形的约束条件:

1.排名的名次应该是连续的,比如第一名和第三名中间一定要有第二名。
2.排名的名次必须并且从开始,不能没有第一名,直接从第二名开始,比如:

无重名:1 2 3 4 5
有重名::1 2 2 3 4 /1 2 3 3 4

这样的结果就一定是错误的:2 2 3 4 5

所以解决问题的矛头又指向了如何判断一个结果是否满足上边的2个条件。

其实,这两个条件不需要分别判断,可以用一个方法同时判断。

首先,假设跳水排名为:a = 4, b = 2, c = 3, d = 1, e = 1.然后我们定义一个int 型的数据ret,初始化为0。然后根据排名对其比特位中的数值做以下更改:

4

这样一来,如果a,b,c,d,e的名字是连续的话,其低位的比特位就全是1.

所以我们可以得到解决办法:

先通过a,b,c,d,e的名字将ret中对应比特位置1,然后判断其低位有没有夹杂0。有0的结果就可以被我们过滤掉了。

至于如何对比特位进行操作使其置1,又如何判断0.可以看这篇博文:

C语言——确定某数比特位中1的个数并打印其32位比特数值


Code(V2.0)


利用上边的思想,我们对for循环最里层的代码做出如下改善:

                        if (((b == 1) + (a == 3) == 1)
                            && ((b == 2) + (e == 4) == 1)
                            && ((c == 1) + (d == 2) == 1)
                            && ((c == 5) + (d == 3) == 1)
                            && ((e == 4) + (a == 1) == 1))
                            //所有的条件都只有1个为真
                        {
                            int ret = 0;
                            //置位
                            ret |= (1 << (a - 1));
                            ret |= (1 << (b - 1));
                            ret |= (1 << (c - 1));
                            ret |= (1 << (d - 1));
                            ret |= (1 << (e - 1));
                            //判断低位中是否夹杂了0
                            while (ret)//直到所有比特位全判断完成才退出
                            {
                                if (ret % 2 == 0)
                                {
                                    break;
                                }
                                ret /= 2;
                            }
                            if (ret ==0)//ret不断/2最终为0时就说明已经判断完了
                            printf("a= %d, b= %d, c= %d, d= %d, e=%d\n", a, b, c, d, e);
                        }

之后运行代码,就可以得到如下结果:

5

可以看到,不用上边投机取巧的做法,我们也能得到正确的结果。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值