久伴说来太遥远,看着背影渐远,不如怀念。
问题描述:现在有n(n>=2)个球,n个球外观一模一样,但是重量有区别,其中有且仅有一个球的重量比其它n-1个球要重,现在有一个天平,天平是完好无损的,问最少需要称多少次才能确定哪个球的重量较重?
灵感来源:http://www.cnblogs.com/dolphin0520/archive/2012/10/02/2707762.html(博客园:海子)
这个问题以前在面试的时候经常遇到,不过记得当时好像没有要求写代码,好像需要推导公式...
一般在遇到在这个问题的时候,思维会很固定的想到去将n个球平分,每次平分下去称量比较。前两天偶尔又碰见这个问题,博主的思路是将n个球分成2组或者3组来解决。
个人按照思路归纳了一遍,如果想要写代码实现统一的实现,肯定要有一个固定的模板。于是我的做法就是n>=2的时候,都是分成3组来解决:
声明:试验证明,平分成2组比较次数远多于3组。
推导思路:(主要这么推导写代码方便)将n个球分成3组。
n=2 : (1,1,0) 最少比较1次
n=3 : (1,1,1) 最少比较1次
n=4: (2,1,1) 最少比较1次 最多比较2次
n=5: (2,2,1) 最少比较1次 最多比较2次
n=6: (2,2,2) 最少比较2次 最多比较2次
n=7: (3,2,2) 最少比较2次 最多比较2次
n=8: (3,3,2) 最少比较2次 最多比较2次
n=9: (3,3,3) 最少比较2次 最多比较2次
n=10: (4,3,3) 最少比较2次 最多比较3次
n=11: (4,4,3) 最少比较2次 最多比较3次
规律:设3组的球个数分别是:(i,j,k)
那么:如果 n%3=0 则 i=n/3 否则 i=n/3+1
剩下 n=n-i;
对于j:如果 n%2=0 则 j=n/2 否则 j=n/2+1
对于k: k=n-i-j
如此下来,在n>=2的时候,将n个球分成3组,每组的个数由以上规则确定出来!
可以看出来,在这么分组后,每组的数据组成大概都会有规律:
当n%3=0, 分组为(n/3,n/3,n/3)
当n%3!=0, 分组为(x,x,y)或者(x,y,y)
按照分为3组的思路,最少次数应该为 3x <=n 即 x=n1/3 取整
java代码:(递归)
从键盘录入一个数字,按回车即可显示出分组情况以及最后的结果,建议n>3,因为代码没有添加太多数字不合格方面的校验。
import java.util.Scanner; /** * there are many balls .find the weighter ball * get the time that find the weighter ball(max time) * */ public class WeightestBall { /** * assign group 将n个数分成m组 * */ private static void assignGroup(int[] array,int n,int m){ if(n<2) return; if(m==3){ if(n%3==0){ array[0]=array[1]=array[2]=n/3; }else{ array[0]=n/3+1; /*剩余的2组继续通过回调赋值*/ assignGroup(array,n-(n/3+1),2); } } if(m==2){ if(n%2==0){ array[1]=array[2]=n/2; }else{ array[1]=n/2+1; array[2]=n-array[1]-array[2]; } } System.out.println(" assgin group result"); for(int i=0;i<array.length;i++){ System.out.print(array[i]+" "); } } /** * compare and get the time 找出最重一个球所需的次数 * */ private static int getMaxWeight(int[] array){ int result=0; /*1 sort: small->big */ // sort(array); int i=array[0],j=array[1],k=array[2]; if(i==j&&j==k){ result=1+f(i); } else{ /*这里会有区别:这里可以求出得到质量不同球的最大/最小次数:此处求最小次数*/ if(f(i)<=f(k)) result=1+f(i); else result=1+f(k); } System.out.println(" need the least "+result+" times"); return result; } /** * f():关键的递归回调函数:按照同样的规则逐层求出所需的次数 * */ private static int f(int n){ int result=0; if(n==2) return 1; if(n==1) return 0; int[] array=new int[3]; assignGroup(array,n,3); result=getMaxWeight(array); return result; } /** * sort 排序:按照从小到大的顺序排列数组 * */ private static void sort(int[] array){ /*select sort:选择排序*/ for(int i=0;i<array.length;i++){ int min=i; for(int j=i+1;j<array.length;j++){ if(array[min]>array[j]){ min=j; } } int temp=array[i]; array[i]=array[min]; array[min]=temp; } System.out.println(" sort result"); for(int i=0;i<array.length;i++){ System.out.print(array[i]+" "); } } public static void main(String[] args) { System.out.println("please input the ball number:"); @SuppressWarnings("resource") int num=new Scanner(System.in).nextInt(); int[] array=new int[3]; if(num>2){ assignGroup(array,num,3); getMaxWeight(array); } } }
明确思路,代码看起来就很简单了。
执行效果:
欢迎有不同看法的同仁指正。转载请注明链接。