第九届蓝桥杯B组第四题–测试次数(C语言)
一.比赛题目
1.题目要求
x星球的居民脾气不太好,但好在他们生气的时候唯一的异常举动是:摔手机。
各大厂商也就纷纷推出各种耐摔型手机。x星球的质监局规定了手机必须经过耐摔测试,并且评定出一个耐摔指数来,之后才允许上市流通。x星球有很多高耸入云的高塔,刚好可以用来做耐摔测试。塔的每一层高度都是一样的,与地球上稍有不同的是,他们的第一层不是地面,而是相当于我们的2楼。如果手机从第7层扔下去没摔坏,但第8层摔坏了,则手机耐摔指数=7。特别地,如果手机从第1层扔下去就坏了,则耐摔指数=0。如果到了塔的最高层第n层扔没摔坏,则耐摔指数=n;为了减少测试次数,从每个厂家抽样3部手机参加测试。
某次测试的塔高为1000层,如果我们总是采用最佳策略,在最坏的运气下最多需要测试多少次才能确定手机的耐摔指数呢?
2.输入与输出
输出:
填写最多测试次数;
二.分析过程
答案为:19
1.方法分析
我想很多人最开始和我一样都会想用二分法来做这个题,但是二分的话,500层一摔手机碎了,250碎了,125碎了;然后三个手机碎完,我们都不知道手机耐摔指数;
而这道题和区间动态规划特别像,所以就用动态规划的方法来做。
2.做题思路
假如动态规划的话,我们首先确定状态变量:
i | 楼层数 |
---|---|
j | 手机数 |
k | 机会数 |
假如我们有k次机会来砸的话:
我们来小数目填表:
手机数\楼层 | 1 | 2 | 3 | 4 |
---|---|---|---|---|
1 | 1 | 2 | 3 | 4 |
2 | 1 | |||
3 | 1 |
因为一部手机的话只能一层楼一层楼来砸,而一层楼的话显然只需要砸一次,所以这个时候我们也把动态规划的边界写出来了(如上表所示);
我们通过上面流程图的分析也可以知道他的状态转移方程如下:
dp[i][j]=min(dp[i][j],max(dp[k-1][j-1],dp[i-k][j])+1);
为什么要用min比较一次?
因为题目中说的是最多,也就是求每次dp[i][j]可能次数里面最小的;
3.核心代码
for(int i=1;i<=n;i++)
for(int j=2;j<=m;j++)
{
dp[i][j]=dp[i][j-1];
//先把dp[i][j]赋值为前一种手机的相同楼层数的最多所需次数
//因为手机比前面多的情况下,最坏情况也不会超过前一种情况
for(int k=1;k<=i;k++)
//砸的机会数不会大于楼层数
dp[i][j]=min(dp[i][j],max(dp[k-1][j-1],dp[i-k][j])+1);
}
三.完整代码
#include<stdio.h>
#include<string.h>
int dp[1010][1010];
int max(int a,int b)
{
return a>b?a:b;
}
int min(int a,int b)
{
return a<b?a:b;
}
int main()
{
int n=1000,m=3;
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;i++)
dp[i][1]=i;
for(int i=1;i<=m;i++)
dp[1][i]=1;
for(int i=1;i<=n;i++)
for(int j=2;j<=m;j++)
{
dp[i][j]=dp[i][j-1];
for(int k=1;k<=i;k++)
dp[i][j]=min(dp[i][j],max(dp[k-1][j-1],dp[i-k][j])+1);
}
printf("%d",dp[1000][3]);
return 0;
}
四.总结
这个代码还没有去优化他的时间复杂度和空间复杂度,这道题也和动态规划里面的砸鸡蛋就一模一样的。