猜数字游戏
20、甲、乙两个人在玩猜数字游戏,甲随机写了一个数字,在[1,100]区间之内,将这个数字写在了一张纸上,然后乙来猜。
如果乙猜的数字偏小的话,甲会提示:“数字偏小”
一旦乙猜的数字偏大的话,甲以后就再也不会提示了,只会回答“猜对 或 猜错”
问: 乙至少猜 多少次 猜可以准确猜出这个数字,在这种策略下, 乙猜的第一个数字是多少???
答案:猜测序列是14,、27、39、50、60、69、77、84、90、95、99
因为无论第几次猜大了,最终的总次数总是14。 这个题目类似于一道Google面试题 : 扔玻璃球求最高楼层。。
一道关于动态规划的面试题——Google面试题:扔玻璃珠
某幢大楼有100层。你手里有两颗一模一样的玻璃珠。当你拿着玻璃珠在某一层往下扔的时候,一定会有两个结果,玻璃珠碎了或者没碎。这幢大楼有个临界楼层。低于它的楼层,往下扔玻璃珠,玻璃珠不会碎,等于或高于它的楼层,扔下玻璃珠,玻璃珠一定会碎。玻璃珠碎了就不能再扔。现在让你设计一种方式,使得在该方式下,最坏的情况扔的次数比其他任何方式最坏的次数都少。也就是设计一种最有效的方式。
首先,为了保存下一颗玻璃珠自己玩,就采用最笨的办法吧:从第一层开始试,每次增加一层,当哪一层扔下玻璃珠后碎掉了,也就知道了。不过最坏的情况扔的次数可能为100。
当然,为了这一颗玻璃珠代价也高了点,还是采取另外一种办法吧。随便挑一层,假如为N层,扔下去后,如果碎了,那就只能从第一层开始试了,最坏的情况可能为N。假如没碎,就一次增加一层继续扔吧,这时最坏的情况为100-N。也就是说,采用这种办法,最坏的情况为max{N, 100-N+1}。之所以要加一,是因为第一次是从第N层开始扔。
不过还是觉得不够好,运气好的话,挑到的N可能刚好是临界楼层,运气不好的话,要扔的次数还是很多。不过回过头看看第二种方式,有没有什么发现。假如没摔的话,不如不要一次增加一层继续扔吧,而是采取另外一种方式:把问题转换为100-N,在这里面找临界楼层,这样不就把问题转换成用递归的方式来解决吗?看下面:
假如结果都保存在F[101]这个数组里面,那么:
F[N]=100-N,
F[100]=min(max(1,1+F[N-1]),max(2,1+F[N-2]),……,max(N-1,1+F[1]));
看出来了没有,其实最终就是利用动态规划来解决这个问题。
折腾n久才把题目的思路想通。
网上题目给的解释的东西总是不怎么符合自己的口味了(有时候还误人子弟啊),老让人想好半天,这时老感觉自己XQ不够用啊,要是可开挂就好了!哎~什么时候开看到些更基础些的呢~毕竟动态规划的东西太灵活了。需要好好想想才可以得出结果啊。
状态:F(N)相当于判断N层楼需要玻璃珠个最少的个数。如果在这N层中的第i层扔一个玻璃球,最多的此次就位max{i, F[N-i]+1},(注意加一,加上本次扔的玻珠,我就忘了这个。)然后在这N层中每一层试试看。找出最小次数作为F(N)={ min( max(1, F(N-1)+1), max(2, F(N-2)+1), ... ,max(N-1, F(1)+1) ) }。终止的条件:F(0)=0,F(1)=1。根据动态规划的思想,使用一个数组保存每个子问题的解,需要时直接取出。
然后又是纸写代码。华丽的递归出来后,调试,错错错。。。
咋一下,递归太深,改非递归。几个细节错误。。
1 #define MAX(A,B) ((A)>(B)?(A):(B)) 2 3 int func12(int N) // 4 { 5 int *F; 6 int min; 7 if (N<=0) 8 { 9 return -1; 10 } 11 F = (int *)malloc((sizeof(int)*(N+1))); 12 memset(F, 0, sizeof(int)*(N+1)); //初始化 13 F[0]=0; 14 F[1]= 1; 15 16 for (int i=2; i<=N; i++) 17 { 18 min=N; 19 for (int j=1; j<=i; j++) 20 { 21 int tmp = MAX(j, F[i-j]+1); 22 if (min>tmp) 23 { 24 min = tmp; 25 } 26 } 27 F[i] = min; 28 } 29 int k = F[N]; 30 31 free(F); 32 33 return k; 34 } 35 36 //调用 37 38 cout<<func12(100)<<endl;
递归和回溯等思想还没有真正学会其精髓啊~
不说了,说多了都是泪。。。。
毕。