(第九届蓝桥杯省赛)耐摔指数(动态规划)

104 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

题目链接:活动 - AcWing

image.png 分析:首先说明一个理解易错点:测试次数是按照单个手机算的,也就是说假如两个手机同时测耐摔指数那么这样算2次,手机个数只是为了增加测试的容错率,假如只有一个手机,那么我们只能从高度为1的地方往上一层一层测试,因为这样要避免手机摔坏而导致无法继续测试,而有两个手机的话我们就可以让第一个手机在任意高度测试,因为还有另一个手机作为备用机。

下面来对这道题目做下分析:

f[i][j]表示有i个手机在高度为j的塔上测试耐摔指数所需要的最少测试次数

由状态的表示含义显然有f[1][i]=i,答案是f[3][n]

那怎么进行状态转移呢?对于f[i][j],假如我们拿第1个手机在第k层测试,这个时候有两种结果,第一种结果是手机摔坏,那么这个时候只剩了i-1个手机,且我们可以知道耐摔指数一定小于等于k,那么剩余需要的最小测试次数就是f[i-1][k-1],加上这一次的测试次数,总的测试次数就是f[i-1][k-1]+1,那如果要是没有摔坏,那这个时候还是i个手机,且我们知道耐摔指数一定大于等于k,在耐摔指数为[k,n]的区间测试和在耐摔指数为[0,n-k]的区间测试是等价的,那么总的测试次数就是f[i][n-k]+1,由于我们要考虑最坏情况,也就是说我们要取这两种情况的最大值,我们唯一能选择的就是在第几层进行测试,也就是k值,所以我们可以遍历所有的k<=n,求所有最坏情况的最小值,这样就可以求得答案,总的复杂度是O(n^2) 下面是代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<queue>
#include<stack>
#include<cstring> 
#include<map>
using namespace std;
const int N=10003;
int f[5][N];//f[i][j]表示有i个手机在高度为j的塔上测试耐摔指数所需要的最少测试次数 
int main()
{
	int n;
	cin>>n;  
	memset(f,0x3f,sizeof f);//初始化
	for(int i=1;i<=n;i++) f[1][i]=i;//初始化
	f[1][0]=f[2][0]=f[3][0]=0;//初始化 
	for(int i=1;i<=n;i++)
	for(int j=1;j<=i;j++)
		f[2][i]=min(f[2][i],max(f[2][j-1],f[1][i-j])+1);
	for(int i=1;i<=n;i++)
	for(int j=1;j<=i;j++)
		f[3][i]=min(f[3][i],max(f[3][j-1],f[2][i-j])+1);	
	cout<<f[3][n];
	return 0;
}