【问题描述】
有一堆共n枚硬币,其中一枚是假币,外观上无法区分,只知道假币的重量稍轻。要求仅使用一个天平,使用最少的重量比较次数找出假硬币。
将n个硬币分成数量相同的两堆,如果n为偶数,每堆的硬币个数为n/2;
如果n为奇数,每堆的硬币个数为(n-1)/2,两堆之外还会剩余一个硬币。
将两堆硬币上天平比较重量,如果有一堆较轻,那么假的硬币必然在轻的那一堆中。如果两堆硬币重量相等,且两堆之外有一个剩余硬币,则那个剩余硬币就是假硬币。如果两堆硬币重量相等,且两堆之外没有剩余硬币,则查找任务失败,未发现假硬币。
编写函数findFalseCoin(coins,start,n)并调用
实现"在读入的coins列表中,从下标start开始的n个硬币中查找假硬币"
【输入形式】
【输出形式】
【样例输入】
100,100,100,99,100,100,100,100,100
【样例输出】
Fake coin:3
【样例说明】
【评分标准】
方法一:分治法(麻烦且非常非常非常非常非常非常非常非常非常没有必要)
- 首先为每个币编号,然后将所有的币等分为两份,放在天平的两边。这样就将区分假币的问题变为区别两堆币的问题。
- 因为假币分量较轻,因此天平较轻的一侧中一定包含假币。
- 再将较轻的一侧中币等分为两份,重复上述做法。
- 直到剩下两枚银币,便可用天平直接找出假币。
# 在读入的coins列表中,从下标start开始的n个硬币中查找假硬币
def findFcoin(coins, start, n):
if (start == n):
print("Fake coin:{}".format(start));
return
sum1 = 0
sum2 = 0
# 偶数个,用分治法
if ((n - start + 1) % 2 == 0):
for i in range(start, n // 2 + 1):
sum1 = sum1 + coins[i]
for j in range(n // 2 + 1, n + 1):
sum2 = sum2 + coins[j]
if sum1 == sum2:
print("Fake coin is not found");
return
elif sum1 < sum2:
findFcoin(coins, start, n // 2) # 用前半段
else:
findFcoin(coins, n // 2 + 1, n) # 用后半段
# 反复调用,直到只剩一个元素
# 奇数个,用遍历
else:
min = coins[start] # 一开始就把第一个元素视为最小
flag = 0 # 判断标志
for i in range(start, n + 1):
if coins[i] < min:
flag = 1 # 最小值存在,判断标志改变
min = i # 记录新的最小值的下标
break
elif coins[i] > min:
flag = 1 # 最小值存在,判断标志改变
min = start # 遍历过后还是起始元素最小
break
# 遍历过程中,元素的值一直都相等,flag=0还是不变
if flag == 1:
print("Fake coin:{}".format(min))
else:
print("Fake coin is not found");
return
coins = list(input().split(','))
coins = [int(i) for i in coins]
findFcoin(coins,0,len(coins)-1)
方法二:直接遍历(简单且通俗易懂)
直接从第一张纸币遍历所有纸币,找到质量小的纸币就返回下标,否则返回“找不到”
def findFalseCoin(coins, start, n):
min = coins[start]
flag = 0
for i in range(start, n + 1):
if coins[i] < min:
flag = 1
min = i
break
elif coins[i] > min:
flag = 1
min = start
break
if flag == 1:
print("Fake coin:{}".format(min))
else:
print("Fake coin is not found");
return
coins = list(input().split(','))
coins = [int(i) for i in coins]
findFalseCoin(coins, 0, len(coins) - 1)