2018年蓝桥杯---测试次数

题目:测试次数

x星球的居民脾气不太好,但好在他们生气的时候唯一的异常举动是:摔手机。
各大厂商也就纷纷推出各种耐摔型手机。x星球的质监局规定了手机必须经过耐摔测试,并且评定出一个耐摔指数来,之后才允许上市流通。
x星球有很多高耸入云的高塔,刚好可以用来做耐摔测试。塔的每一层高度都是一样的,与地球上稍有不同的是,他们的第一层不是地面,而是相当于我们的2楼。
如果手机从第7层扔下去没摔坏,但第8层摔坏了,则手机耐摔指数=7。
特别地,如果手机从第1层扔下去就坏了,则耐摔指数=0。
如果到了塔的最高层第n层扔没摔坏,则耐摔指数=n
为了减少测试次数,从每个厂家抽样3部手机参加测试。
某次测试的塔高为1000层,如果我们总是采用最佳策略,在最坏的运气下最多需要测试多少次才能确定手机的耐摔指数呢?
请填写这个最多测试次数。
注意:需要填写的是一个整数,不要填写任何多余内容。

答案:19

解题思路:看到这道题的第一反应很多人都会想起来用二分法查找,那么答案的话会相当简单 log21000=10。
1.可是题目是要测试出手机的耐摔指数,如果用二分法第一次在500层摔下去没碎,下一次在250层没碎,再下一次125层碎了,那我们这个手 机的耐摔指数是多少呢,显然我们并不能得知,所以要采用二分法进行手机的耐摔指数,需要10部手机才能在最坏的情况下测出耐摔指数。
2.所以题目的正确思路应该是使用动态规划的方法

a.列表格
在这里插入图片描述 图中白色空格中内容为(仅有i层楼时利用j个手机时题目所求的最佳策略最坏运气下的)摔手机次数

b. 1.填数据(当我们只有一部手机的时候)

//只有一个手机时,几层楼就要摔几次
		for(int i=1;i<=N;i++) {
			dp[i][1]=i;
		}

在这里插入图片描述
2.当我们的手机数有两部和三部的时候

题目给出了:为了减少测试次数,从每个厂家抽样3部手机参加测试。这段话应该怎么理解呢
例如只有一部手机的时候我们只能一层一层的摔,1000层的话最坏情况需要1000次

  • 1.当我们有两部手机时在有3层楼的情况下我们就能体会到减少测试次数
  • 2.当我们把一部手机从2层摔下时,有两种情况要么碎了,要么没碎。
    此时只需再摔一次就可以得到耐摔指数了.(在2层楼碎了的情况下往1层楼摔一次,
    碎了则代表耐摔指数是0,没坏就是1。在2层楼摔下去没碎的情况同理) (当我们有3部手机3层楼的情况同理)
  • 此时我们可以考虑只有一个手机3层楼的情况,我们此时并不能往2层楼扔下去, 因为它碎了,我们
  • 就无法得知他的耐摔指数了,此时我们只能乖乖的从1层开始往下扔.

具体的表格数据
在这里插入图片描述
c.总结规律:

dp[i][j]: 表示j部手机从i层楼摔下去的测试此时
由于我们可以确定每个空的最大值为其前一楼层次数+1(例如:求2个手机,3楼的次数时,在求2楼的条件下在3楼再摔一次一定可以得到3楼的次数) 即 dp[i-1][j]+1

最小值有两种情况:
损坏:继续尝试共k-1层,手机数-1: dp[k-1][j-1]
未损坏:继续尝试共N-K层,手机数不变:dp[n-k][j]

最少次数从上述两种情况中产生,由于要求运气最差即:max(损坏情况次数,未损坏情况次数)

且两者都在第K层尝试过一次max(损坏情况次数+1,未损坏情况次数+1)

即:max(dp[k-1][j-1]+1,dp[n-k][j]+1) => max(dp[k-1][j-1],dp[n-k][j])+1

max(dp[k-1][j-1],dp[n-k][j])+1 <= dp[i][j] <=dp[i-1][j]+1

最后结果存在dp[1000][3]中,由于我们填表的过程中,是按行优先填的(手机数一定情况下,楼层变化,这样做是因为每空最大值的确定依赖于它前面一空(手机数不变,楼层-1))所以实现的代码也是如此(手机数在外层循环):(注意:我们的策略必须是最优的,即使运气差,得到的只是可选项差,不会影响我们从可选项中挑选最优的解,即次数最少的解,所以我们需要判断当前选项哪一个最优即:min())

具体代码:

package 蓝桥杯_2018;

import java.util.Scanner;
/**
 * 题目理解:
 * 在最坏的运气下最多需要测试多少次才能确定手机的耐摔指数呢?
 * 1.这里的测试多少次是指需要摔多少次手机
 * 2.个人一开始疑惑的点是为了减少测试次数,从每个厂家抽样3部手机参加测试
 * (个人理解的偏差以为一次测试可以扔3部手机),1次测试还是只能扔1部手机
 *   其实题目增加手机数是为了尽量少摔几次手机
 *     1  2  3  4  5  6 ....1000
 *  1  1  2  3  4  5  6     1000      dp[i][j]:表示i层楼时使用j部手机需要摔的最大次数
 *  2  1  2  2
 *  3  
 *  例如只有一部手机的时候我们只能一层一层的摔,1000层的话最坏情况需要1000次
 *  1.当我们有两部手机时在有3层楼的情况下我们就能体会到减少测试次数
 *  2.当我们把一部手机从2层摔下时,有两种情况要么碎了,要么没碎。
 *         此时只需再摔一次就可以得到耐摔指数了.(在2层楼碎了的情况下往1层楼摔一次,
 *         碎了则代表指数是0,没坏就是1。在2层楼摔下去没碎的情况同理) 
 *   此时我们可以考虑只有一个手机3层楼的情况,我们此时并不能往2层楼扔下去, 因为它碎了,我们
 *   就无法得知他的耐摔指数了,此时我们只能乖乖的从1层开始往下扔.      
 */
public class 测试次数 {

	public static void main(String[] args) {
		
		Scanner s=new Scanner(System.in);
		int N=s.nextInt();  //表示楼层数
		int M=s.nextInt();  //表示手机数
		
		//dp[i][j]:表示i层楼时使用j部手机需要摔的最大次数
		int dp[][]=new int[N+1][M+1];
		
		//只有一个手机时,几层楼就要摔几次
		for(int i=1;i<=N;i++) {
			dp[i][1]=i;
		}
		
		//两个以及以上手机时(j代表剩余手机数,i代表剩余楼层数)
		for(int j=2;j<=M;j++) {
			for(int i=1;i<=N;i++) {
				
				//赋最大初值(楼层每增加一层,其需要摔的次数一定会小于等于其楼层数减一的次数+1)
				dp[i][j]=dp[i-1][j]+1;
				
				for(int k=2;k<i;k++) {
					//dp[k-1][j-1]表示在第k层摔坏了    
					//dp[i-k][j]表示第k层没摔坏
					dp[i][j]=Math.min(dp[i][j],
							Math.max(dp[k-1][j-1], dp[i-k][j])+1);
				}
			}
		}
		//输出表格右下角的值
		System.out.println(dp[N][M]);
		s.close();

	}

}

发布了19 篇原创文章 · 获赞 2 · 访问量 423

猜你喜欢

转载自blog.csdn.net/TheWindOfSon/article/details/103181774