Bilibili 1024: https://b23.tv/PjP70N

 

技术对抗赛


 

算法与安全答题


 

1. 给定有 n 瓶水和4只老鼠,n 瓶水中 最多有1瓶有毒,老鼠喝了有毒的水之后(无论剂量)1天后就会死亡,问如果只给定一天的时间来测试哪一瓶水有毒,n 最大值为多少?

A. 9      B.12   C. 15   D.16

解题思路

直觉来想这个问题,如果不限制一天时间,一个确定多瓶水中哪一瓶有毒的思路是通过2分法,每一次1只老鼠喝一半数量瓶数的水的混合成品后,即可定位一半瓶数的水是否有毒。这样,对于16瓶水来说,第一天定位到8瓶内,第二天定位到4瓶内,第三天定位到2瓶内,第四天过完后,定位到最终是哪一瓶有毒。这种方法,需要4天确定16瓶子水中哪一瓶有毒,最多需要4死只老鼠,最好的情况1只老鼠都不用死。

很明显,这种思路不符合题目只给一天的时间的要求。仔细审题,重点:“只给一天时间”,“老鼠喝到毒需要一天时间才能知道是否有毒”

老鼠喝水,要么死(1),要么活(0),也就是单独一只老鼠能提供的信息状态有 1或0 两个状态。此时最多只能用来检测1瓶水是否有毒。

两只老鼠,各自喝水,也是要么死要么活,此时两只老鼠能提供的信息状态为 4 个,因为此时要么都没死(11),  要么都没死(00), 要么一死一活(10 或 01),这是两只老鼠喝水能够提供的最大信息状态数。如何设计实验,让老鼠喝水能在一天时间得到这些信息状态的可能呢?

尝试一

我们给3瓶水编号 1,2,3. 第一只(A)喝1号,第二只老鼠(B)喝2号,然后等一天,两只都死(11)的情况是不可能的,因为这里最多有1瓶有毒;如果一死一活(10 或 01),可以分别确定1号或2号有毒;如果两只都没死(00),那只能确定1和2没毒,但是3有没有毒由于没有老鼠去喝,所以不能确定3号有没有毒。换句话说,这个尝试的方案无法百分百确定3瓶水中,没被喝的那瓶水的情况。我们发现,这里(10)(01)(00) 三种信息状态被用于推理,而(11)这种信息状态被浪费了,2只老鼠是否死亡的情况能提供的4个信息状态,只用了3个。

尝试二

我们可以给3瓶水编号 1,2,3. 从二进制看这个编号分别为 01,10,11.  如果我们把1号给第二只老鼠(B)喝,2号给第一只(A),3号水同时给两只老鼠(AB)喝,

老鼠代号   A    B

编号(  1)    0    1     给 B

编号(  2)    1    0     给 A

编号(  3)    1    1     给 AB

这样,如果 AB 都死了(11),说明 3 号有毒,因为 3号都给了 AB;如果A死B没死(10),说明 2 号有毒,因为 B 喝了1和3号,只有2号没喝;如果A没死B死(01),说明 1号有毒,因为 A喝了2和3,只有 1 没喝;如果AB都没死(00),则说明3瓶水都没毒,因为A喝了2和3,B喝了1和3,如果有其中任意一瓶有毒,AB一定有死的,所以只能说明每一瓶都没毒。

总结:

通过上面的设计,我们得到了这样一个方案,能够用上2只老鼠是否死亡的情况能提供的所有 4 个信息状态,1天之内,通过以恰当的方式给老鼠喝不同的水,通过每只老鼠是否死亡,可以明确确定 3 瓶水中是否全没毒,或哪一瓶水有毒。

可以看到,上面使用的这种 “恰当的方式” 为:将瓶子从1开始编号,从二进制的角度来看编号,每个二进制位对应一只老鼠(3个位数对应3只,4个位数对应4只),每瓶水给其编号中二进制数为 1的那一位老鼠喝,这样一天后,每一位老鼠的死和活对应于1和0,连起来构成的老鼠个数的位置的二进制数,即定位了唯一的那一瓶水。

不难发现,1只老鼠最多用来定位1瓶水的情况,2只老鼠最多定位 2^2-1=3瓶水的情况,3只老鼠最多定位2^3-1=7瓶水,4只老鼠最多定位 2^4-1=15瓶水的情况,n只老鼠最多定位 2^n-1瓶水的情况

网友评论

网友说为什么不可以是 16 瓶

这里比需要搞清楚这个解决方案的本质,4只老鼠的生死(01)最多在1天时间内最多只能给出 2^4 = 16 种状态,其中 0000 表示的是每只老鼠都喝了,但是每一瓶都没有毒,所以没有老鼠死去。

注意这种情况并不多余,因为题目中说:最多有1瓶有毒 (不是必有一瓶毒药,而是可以都没毒,有毒的话也最多1瓶).

如果给定 16 瓶水和4只老鼠的话,按照这个实验思路,4 个二进制位(4只老鼠), 0000~1111 这16个状态(对应 "没有1瓶有毒" 1种状态 或 "瓶子编号1~15某一瓶有毒" 15种状态)位已经不足以承载 17 个信息量("没有1瓶有毒" 1种状态 或 "瓶子编号1~16某一瓶有毒" 16种状态),这时必然需要多 1 只老鼠这个解答思路才有可能。


 

2. 计算100到10000(包含10000)所有偶数相加结果。(本题由UP主@python学习者提供)
A:  20002550
B:  25002550
C:  20502550
D:  25002050

解题思路:100 + (10000+102)x(10000-100)/2/2


 

3.期末考试结束了,老师决定带学生们去卷饼店吃烤鸭饼。老师看到大饼和鸭子,搞了一个活动:每人可以拿走一张饼,谁卷到的食物美味程度总和最高,谁就能获得称号:卷王之王!Vita很想得到“卷王之王”称号,他的大饼可以装下大小总和不超过500的食物,现在有7块鸭肉和6根黄瓜,每份食物都有它的大小和美味程度。 每块鸭肉的大小:85、86、73、66、114、51、99 每块鸭肉的美味程度:71、103、44、87、112、78、36 每根黄瓜的大小:35、44、27、41、65、38 每块黄瓜的美味程度:41、46、13、74、71、27 老师要求大饼里至少有一块鸭肉和一根黄瓜。请问,Vita卷到的食物美味程度总和最大是多少?(本题由UP主@小学生Vita君提供)

A. 593   B.612    C.496   D. 584

解题思路:01 背包算法(01背包问题 之 动态规划(通俗解释)

容器 500

鸭肉 :85、  86、 73、66、114、51、99

美味: 71、103、44、87、112、78、36

黄瓜: 35、 44、  27、  41、 65、 38

美味: 41、 46、  13、  74、  71、 27

#include <vector>
#include <iostream>

//解决方案
class solution
{
public:
    solution(int value):totalValue(value) {}
    int totalValue;             //选择的物品的总价值
    std::vector<size_t> items;  //选择的物品的项
    int containerValue;         //容器容量
};

//构建网格
solution buildNet(const std::vector<size_t>& w, const std::vector<int>& v, size_t total)
{
    size_t row = w.size();      //可选择的物体数量
    size_t column = total;      //总容量

    std::vector<std::vector<solution>> net;
    net = std::vector<std::vector<solution>>(row+1, std::vector<solution>(column+1, 0));  //初始化多第一行和第一列,便于通用公式

    for (size_t r = 1; r <= row; ++r)
    {
        for (size_t c = 1; c <= column; ++c)
        {
            size_t weightCurrent = w[r - 1]; //当前物品重
            int valueCurrent = v[r - 1];  //当前物品价值
            if (weightCurrent <= c)       //如果单独放得下
            {
                int valueIncludeCurrent = valueCurrent + net[r - 1][c - weightCurrent].totalValue;
                if (valueIncludeCurrent > net[r - 1][c].totalValue) //加入当前物品价值更高,则更新方案
                {
                    net[r][c] = valueIncludeCurrent;

                    net[r][c].items = net[r - 1][c - weightCurrent].items; //得到之前的序列
                    net[r][c].items.push_back(r);                          //添加自己到序列后
                }
                else
                    net[r][c] = net[r - 1][c];
            }
            else
                net[r][c] = net[r - 1][c];
        }
    }

    net[row][column].containerValue = total;
    return net[row][column];
}

//打印选择的最佳方案
void printVector(const std::vector<size_t>& w, const std::vector<int>& v,const solution & s)
{
    std::cout << "Input: ";
    for (size_t i = 0; i < w.size(); ++i)
    {
        std::cout << w[i] << " (" << v[i] << ");" ;
    }
    std::cout << "Container: " << s.containerValue << std::endl;

    const std::vector<size_t>& items = s.items;
    int totalV = s.totalValue;

    size_t totalW = 0;
    size_t totalV2 = 0;
    for (auto r : items)
    {
        size_t w0 = w[r-1];
        int v0 = v[r-1];
        std::cout << w0 << " (" << v0 << ");" << std::endl;

        totalW += w0;
        totalV2 += v0;
    }
    std::cout << "Total: " << totalW << " (" << totalV << " -> check:" << totalV2 << ")";
    std::cout << std::endl << std::endl;
}

int main()
{
     std::vector<size_t> w2 = { 85, 86, 73, 66, 114, 51, 99 };
     std::vector<int> v2 = { 71,103,44, 87, 112, 78, 36 };
     solution duck = buildNet(w2, v2, 500);
     printVector(w2, v2, duck);

     std::vector<size_t> w3 = { 35, 44,  27,  41, 65, 38 };
     std::vector<int> v3 = { 41, 46, 13, 74, 71, 27 };
     solution cucumber = buildNet(w3, v3, 500);
     printVector(w3, v3, cucumber);

     std::vector<size_t> w4 = w2;  w4.insert(w4.end(), w3.begin(), w3.end());
     std::vector<int> v4 = v2; v4.insert(v4.end(), v3.begin(), v3.end());
     solution duckCucumber = buildNet(w4, v4, 500);
     printVector(w4, v4, duckCucumber);

    return 0;
}
算法实现参考

相关文章:

  • 2021-08-05
  • 2021-12-25
  • 2021-08-17
  • 2022-12-23
  • 2021-07-22
  • 2021-06-19
  • 2021-06-14
  • 2022-12-23
猜你喜欢
  • 2021-12-26
  • 2022-01-11
  • 2021-09-04
  • 2021-04-02
  • 2021-05-26
相关资源
相似解决方案