101枚硬币中有一枚假币, 有一个无砝码的天平, 在最坏情况下最少称多少次,可以判断假币比真币重还是轻
首先看一道商汤的笔试题:
- [商汤]101枚硬币中有一枚假币,有一个无砝码的天平,在最坏情况下最少称 多少 次,可以判断假币比真币重还是轻。
解法1: 两次
//Heavier: true, lighter: false
FakeCoin1:
divide coins S into (A: 50, B: 50, C: 1)
if A == B:
return C > A[0]
else:
return min(A,B)[:25] == min(A,B)[25:]
解法2: 两次
//Heavier: true, lighter: false
FakeCoin2:
divide coins S into (A: 33, B: 33, C: 35)
if A == B:
return C > (A+B)[:|C|]
else:
return min(A,B) == C[:|B|]
解法3: 两次
//Heavier: true, lighter: false
FakeCoin3:
divide coins S into (A: 33, B: 33, C: 35)
if A == B:
return C > (A+B)[:|C|]
elif A > B:
return C >= min(A,B)+max(A,B)[:|C|-|A|]
三种解法的共同约束为\(C=S-A-B, |A|=|B|\)
解法1. 要求\(|A|\%2=0, |A|=\lfloor\frac{|S|}2\rfloor\), 显然只能处理数量为4k+1的硬币堆.
a). 如果A==B, 显然C假币. 此时A,B构成类(划分)间、类内等价, 从A,B中任取一个硬币与C比较即可
b). 如果A!=B, 显然C真币. 此时A异或B构成类内等价, 对半划分A或B确定其类内等价性即可
解法2. 要求\(|A|\leq|C|\leq 2|A|\)即 \(3|A| \leq |S| \leq 4|A|\).
a). 如果A==B, C类内等价无假币. (A+B)的子集若较C重则假币较重, 否则较轻
b). 如果A!=B, 使用C的子集与min(A,B)比较, 若相等则假币较重, 否则较轻
解法3. 要求\(|A| \leq |C| \leq 2|A|\) 即 \(3|A| \leq |S| \leq 4|A|\).
a). 如果A==B, C类内等价无假币. (A+B)的子集若较C重则假币较重, 否则较轻
b). 如果A!=B, 使用C的子集与min(A,B)+max(A,B)[:|C|-|A|]比较
min(A,B)包含较轻假币 -> min(A,B)+max(A,B)[:|C|-|A|] < C;
max(A,B)包含较重假币 -> min(A,B)+max(A,B)[:|C|-|A|] >= C;
假币的轻重和比较判定式均是划分(不交不漏)且是双射(互为充要条件)
总结三种解法适用于不同的划分要求, 其中解法1的划分可以直接构造一个类内等价的单元素类但限制是硬币总数为4k+1, \(a_n=2*a_{n-1}+1, a1=3\)序列下的任意一个数可以使得解法一退化成\(O(logN)\)复杂度.对于算法2.3, 其划分要求一致但后判使用了两种不同的策略, 解法3相比解法2更晦涩难懂.