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瓶水的情况
网友评论:
这里比需要搞清楚这个解决方案的本质,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; }