【三分】【AOJ-158】有假币

Description
居然有假币!!!
事情是这样的,现在猪肉涨了,但是农民的工资却不见涨啊,没钱怎么买猪肉啊。渊子这就去买猪肉,结果找来的零钱中有假币!!!可惜渊子一不小心把它混进了一堆真币里面去了。现在知道假币的重量比真币的质量要轻。给你一个天平,请用最快的时间把那个可恶的假币找出来。

Input
1≤n≤2^30,输入0结束程序。
Output
最少要称几次一定能把那个假币找出来。
Sample Input
3
12
0

 
Sample Output
1
3
 
 
思路:
三分法

一、     首先我们先来考虑最简单的问题1。为了方便叙述,把n个硬币按1,2,…,n顺次编号。

二、     若n=3,把一号硬币放在天平左边、二号硬币放在天平右边。如果天平:

三、     左偏,一号重,是假币。

四、     右偏,二号重,是假币。

五、     保持平衡,那么一、二都是正常的硬币,因此就只有可能三号硬币是假币了。

六、     因此n=3,至多一次就能称出哪个是假币。记作f(3)=1。

七、     下面考虑n=9。把所有的硬币分成三组:A{1,2,3},B{4,5,6},C{7,8,9}。A组的硬币放在左边、B组放在右边。如果天平:

八、     左偏,则假币在A组里面。

九、     右偏,则假币在B组里面。

十、     保持平衡,假币在C组里面。

十一、   无论在哪个组里面,我们已经把假币的范围从“9”缩小到了“3”,也就是减少到原来的1/3。之前我们已经研究过,3个硬币1次就能称出来,故而f(9)=2。

十二、   不难推广到一般的情况:

十三、   定理1.1 f(3n)=n。

十四、   证明:n=1,2时,已证。设n=k成立,则f(3k)=k;下面考虑n=k+1的情况。

十五、   将3k+1个硬币分成三堆A, B, C,使得|A|=|B|=|C|=3k。把A放在天平左边、B放在右边,天平:

十六、   左偏,假币在A

十七、   右偏,假币在B

十八、   平衡,假币在C

十九、   无论哪种结果,我们都把假币的范围缩小到了3k个硬币里面。而f(3k)=k,故而f(3k+1)=k+1。

二十、   综上,定理1.1成立。

二十一、稍经分析不难得到:

二十二、定理1.2 f(n)=

二十三、这个的证明和定理1.1完全类似,分n mod 3 = 0, 1, 2适当讨论即可。

二十四、我们必须注意到 是可行的,因为我们能够构造出这样一个方案。问题是它是否最优?

二十五、我们采取的方案是每次将硬币尽量均匀的三分,这样做的根据就是天平只有三种结果:左偏、右偏、平衡。于是就能保证无论假币在哪一份都能将结果的范围缩小到原来的1/3。从感性上认识, 应该就是最优解了。

二十六、为了更加严格的证明 的最优性,我们引进判定树的概念。

二十七、下图就是n=9时的一种判定树:

二十八、此题的判定树是这样一棵树:

二十九、叶子节点代表一种可能的结果。

三十、   非叶子节点代表一次称量。

三十一、非叶子节点至多有三个儿子,分别代表天平的左偏、右偏、平衡三种情况。

三十二、任意一种称量方案都能唯一的表示成一棵判定树;反过来一棵判定树也唯一对应一种称量方案。

三十三、容易看出判定树的深度就是称量次数。这就是我们之所以引进它的原因。

三十四、做出判断之前,谁也无法预知哪个硬币是假币,每个都有可能是我们的目标;因此一个有意义的判定树应该具有至少n个叶子节点。

三十五、 n个叶子节点的树的深度h ≥ ,故而可以证明,f(n)= 是最优的。

三十六、 我们的结论是:有n(n≥3)个硬币,其中一个是假币,假币的重量比其他的要重一些。给一架天平,至少称 次,就能找出那个假币。

三十七、 具体的方案是将硬币每次都尽量均匀的三分。

三十八、 让我们总结一下。

三十九、 “三分”是整个解法的核心。我们选择三分,而不是二分或者四分是有原因的,它的本质是由判定树的特殊结构——三叉树——所决定的。

四十、  同时还必须注意一点,我们在三分的时候有两个字很讲究:“均匀”。实际上h ≥ 中的“=”当且仅当硬币被均匀的分配时才能达到。

四十一、 这里说的“均匀”是指“在最坏情况下获得最好的效果”。因为一棵树的深度是由它根节点儿子中深度最大的儿子决定的,为了使得整个树深度最小,我们就要务必使得深度最大的儿子深度最小,这就是“均匀”分配的理论根据。

 

 

参考代码

#include <stdio.h>
#include <math.h>
int main()
{
    long int n;
    int m;
    while(scanf("%ld",&n)&&n)
    {
        m=(int)ceil(log(n*1.0)/log(3.0));//ceil()向上取整函数 log(n)/log(3)用三分法可证
        printf("%d
",m);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/ahu-shu/p/3471779.html