逻辑思维与C/C++解题

59 篇文章 3 订阅
51 篇文章 0 订阅

谁在说谎

考虑这样一些陈述:

西电有四位同学中的一位做了好事,分别询问,他们的回答分别是:
- A说:不是我
- B说:是C
- C说:是D
- D说:C胡说

已知四人中三人说了真话,一人说的是假话,请问谁说了谎话,谁做的好事。

通过简单的规约我们即可获得答案,B和C有一人说谎,A和D自然说的都是实话,D说:C胡说,则C说慌,C是做好事的人。

我们如何交由计算机实现呢?枚举法,所谓枚举,也即需要在全部可能的空间中进行遍历判断。

#include <iostream>
int main(int, char**)
{
    char space[] = {'A', 'B', 'C', 'D'};
                        // 重要!整个解空间的定义
    int i = 0;
    for(; i < 4; i++)
    {
        int n_honest = (space[i] != 'A') + (space[i] == 'C') + (space[i] == 'D') + !(space[i] == D);
        if (n_honest == 3) 
            break;
    }
    std::cout << space[i] << " did the good things" << std::endl;
    return 0;
}

谁是犯罪嫌疑人

某地刑侦大队对涉及6个嫌疑人的一桩疑案作如下分析:

  • A,B至少有一人作案
  • A, E, F 3人中至少有两人参与作案
  • A, D不可能是同案凡
  • B, C或同时作案,或与本案无关
  • C,D中有且仅有一人作案
  • 如果D没有作案,则E也不可能参与作案

这种情况下,可能性较多,人工的方式可能比较费时,如果能充分利用现代计算机的计算能力(辣么高的主频),将会给类似问题带来新的契机。

仍然采用枚举的方式,对整个可能空间进行遍历式判断。已知一共六个人,每个人都只有“是罪犯”“不是罪犯”两种可能,则6个人组成的可能数 26=64

#include <iostream>
#include <bitset>

int main(int, char**)
{
    bool b1, b2, b3, b4, b5, b6;
    char judge[2][9] = {"不是罪犯", "是罪犯"};
                                    // 中文占两个字节
    for (int i = 0; i < pow(2, 6); ++i)
    {
        std::bitset<6> bs(i);
                        // i = 0  ⇒ [0, 0, 0, 0, 0, 0]
                        // i = 63 ⇒ [1, 1, 1, 1, 1, 1]
                        // bs[0], bs[1], bs[2], bs[3], bs[4], bs[5]
                        // A,     B,     C,     D,     E,     F
        b1 = bs[0]||bs[1];
        b2 = (bs[0]&&bs[4])||(bs[0]&&bs[5])||(bs[4]&&bs[5]);
        b3 = !(bs[0]&&bs[3]);
        b4 = (bs[1]&&bs[2])||(!bs[1]&&!bs[2]);
        b5 = (bs[2]&&!bs[3])||(!bs[2]&&bs[3]);
        b6 = bs[3]||!bs[4];
                                // 非D ⇒ 非E 等价于 D||非E
        if (b1&&b2&&b3&&b4&&b5&&b6)
                                // 每个条件都满足
        {
            std::cout << "A: " << judge[bs[0]] << std::endl;
            std::cout << "B: " << judge[bs[1]] << std::endl;
            std::cout << "C: " << judge[bs[2]] << std::endl;
            std::cout << "D: " << judge[bs[3]] << std::endl;
            std::cout << "E: " << judge[bs[4]] << std::endl;
            std::cout << "F: " << judge[bs[5]] << std::endl;            
            break;
        }
    }
    return 0;
}

每个人只说对了一半

关于五位选手的排名,各做如下的陈述:

  • A:B第二,我第三
  • B:我第二,E第四
  • C:我第一,D第二
  • D:C最后,我第三
  • E:我第四,A第一

每个人对了一半,错了一半。请编程给出正确的名此。

根据排列组合的知识,我们知五个人排名,所有可能性的个数为: A55=120 ,我并没有构造出合适的for循环,控制解空间的大小,无奈暂时只得用五重的for循环+if判断

int b1, b2, b3, b4, b5;
for (int A = 1; A < 6; ++A)
    for (int B = 1; B < 6; ++B)
        for (int C = 1; C < 6; ++C)
            for (int D = 1; D < 6; ++D)
                for(int E = 1; E < 6; ++E)
                {
                                    // 本质上表达的是5^5=3125的概念
                    if (A*B*C*D*E=120)
                    {
                        b1 = (B==2) + (A==3);
                        b2 = (B==2) + (E==4);
                        b3 = (C==1) + (D==2);
                        b4 = (C==5) + (D==3);
                        b5 = (E==4) + (A==1);
                        if (b1*b2*b3*b4*b5==1)
                        {
                            cout << A << B << C << D << E << endl;
                            break;
                        }
                    }
                }

后来在我晚上跑步的时候,丑陋的五重循环始终折磨着我,终于让我想出了如何用组合 A55 的形式,完美的解决这一问题,下述的代码用到了STL<algorithm>的next_permutation,具体请参阅该函数的相关说明:

#include <iostream>
#include <algorithm>                // next_permutation  
#include <iterator>

int main(int, char**)
{
    int ranking[] = {1, 2, 3, 4, 5};

    int b1, b2, b3, b4, b5;
    bool flag = true;

    while(flag)
    {
        b1 = (ranking[1] == 2) + (ranking[0] == 3);
        b2 = (ranking[1] == 2) + (ranking[3] == 4);
        b3 = (ranking[2] == 1) + (ranking[3] == 2);
        b4 = (ranking[2] == 5) + (ranking[3] == 3);
        b5 = (ranking[4] == 4) + (ranking[0] == 1);

        if (b1*b2*b3*b4*b5 == 1)
        {
            std::copy(ranking, ranking+5, std::ostream_iterator(std::cout, " "));
            std::cout << std::endl;
            break;
        }

        flag = std::next_permutation(ranking, ranking+5);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

五道口纳什

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值