蓝桥杯——摔手机 动态规划

题目描述

X 星球的居民脾气不太好,但好在他们生气的时候唯一的异常举动是:摔手机。

各大厂商也就纷纷推出各种耐摔型手机。x星球的质监局规定了手机必须经过耐摔测试,并且评定出一个耐摔指数来,之后才允许上市流通。

X 星球有很多高耸入云的高塔,刚好可以用来做耐摔测试。塔的每一层高度都是一样的,与地球上稍有不同的是,他们的第一层不是地面,而是相当于我们的 222 楼。

如果手机从第 777 层扔下去没摔坏,但第 888 层摔坏了,则手机耐摔指 =7=7=7。 特别地,如果手机从第 111 层扔下去就坏了,则耐摔指数 =0=0=0。 如果到了塔的最高层第 nnn 层扔没摔坏,则耐摔指数 =n=n=n。

为了减少测试次数,从每个厂家抽样 333 部手机参加测试。

某次测试的塔高为 100010001000 层,如果我们总是采用最佳策略,在最坏的运气下最多需要测试多少次才能确定手机的耐摔指数呢?

请填写这个最多测试次数。

运行限制

  • 最大运行时间:1s
  • 最大运行内存: 128M

 

思路:

一开始没理解透题意,果然后面的题,看出来一个easy的答案就可以直接排除了

最优化问题,考虑贪心或dp

先分析一下题目,随便一层扔下去,手机坏了,说明耐摔指数小于k,但不能说明具体是多少

如果现在没有其他手机了,那肯定测不出具体的手机耐摔系数了,所以,当手机数为1的时候,不能随便扔,只能从低到高一层一层的测试。手机多的时候这显然不是最佳策略

如果手机没坏,手机数量不变,显然可以再从上面挑一层扔,缩小可能的系数范围

 

归纳一下,从第k层扔下去,有两种情况:

1.手机碎了,那就说明耐摔指数一定小于k,那么要测0~k-1层

2.手机没碎,那就说明耐摔系数大于等于k,那么要测k+1~n层

并且发现,100~110和10~20两个层段,楼层数相同的情况下,给定相同手机数,两层的最佳策略的摔手机次数是一样的!

比如,两部手机,100~110,最佳策略应该先从中间105扔,运气最差应该是系数为103or104(碎了最麻烦,如果没碎,可以从108继续扔缩小一半区间),那么需要从100依次尝试到104,一共6次;从10~20,最佳策略也应从中间扔,也就是15层,运气最差应该是14or13,需要从10到11,12,13,14,依次尝试,一共也是6次,可以看出只与中间夹的层数有关,

也就是说存在子结构,动态规划

设dp[i][j]为,有j部手机,夹层数为i做测试,从中间第k层扔下,最优策略下的次数

1.手机碎了,dp[i][j]=dp[k-1][j-1]+1

2.手机没碎,dp[i][j]=dp[i-k][j]+1

每次运气最差,也就是说每次都正好选到测试次数最多的那个中间层k,最优策略,即所有最差测试里面最少次数的那个!这个地方也容易错!而且这里错了很冤!

得到递推式为:dp[i][j]=min(dp[i][j],max(dp[i-k][j]+1,dp[k-1][j-1]+1)+1);

注意边界条件,i=0,j=0时应该都是0,并且一部手机的时候层数就是最少取值

#include <bits/stdc++.h>
#include <iostream>
#include <math.h>
#include <stdlib.h>
#include <cstring>
#include <stdio.h>
#include <queue>
#include <algorithm>
#include <vector>
#include <map>
#include <set>
#include <string>

#define MAX 1010
#define INF 0x3f3f3f3f
#define EXP 1e-9
#define DEUBG 0
#define MOD 1000000007

using namespace std;

typedef long long ll;

int n,m,t,k;
string s;
int dp[MAX][MAX];

int main(){
	n=1000;
	m=3;
	memset(dp,0x3f,sizeof(dp));
	for(int i=0;i<=n;i++){
		dp[i][0]=0;
	}
	for(int i=0;i<=m;i++){
		dp[0][i]=0;
	}
	for(int i=1;i<=n;i++){
		dp[i][1]=i;
	}
	for(int j=2;j<=m;j++){
		for(int i=1;i<=n;i++){
			for(int k=2;k<=i;k++)
				dp[i][j]=min(dp[i][j],max(dp[k-1][j-1],dp[i-k][j])+1); 
		}
	}
	printf("%d\n",dp[n][m]);
	return 0;
}

 

举一反三,运气最好情况下最优策略,即每次挑中都是最好的楼层,测试次数最少,所有最少次数里最少次数的那个 dp[i][j]=min(dp[i][j],min(dp[i-k][j]+1,dp[k-1][j-1]+1)+1);

运气最差情况下的最差策略,所有最多测试次数里测试次数最多的   dp[i][j]=max(dp[i][j],max(dp[i-k][j]+1,dp[k-1][j-1]+1)+1);

运气最好,最差策略,所有最少次数里最多次那个dp[i][j]=max(dp[i][j],min(dp[i-k][j]+1,dp[k-1][j-1]+1)+1);

 

 

 

 

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Java中,带分数可以用分数类来表示。下面是一个简单的带分数类的实现: ```java public class Fraction { private int integer; private int numerator; private int denominator; public Fraction(int integer, int numerator, int denominator) { this.integer = integer; this.numerator = numerator; this.denominator = denominator; simplify(); } private void simplify() { if (numerator < 0 && denominator < 0) { numerator = -numerator; denominator = -denominator; } if (denominator < 0) { numerator = -numerator; denominator = -denominator; } if (integer < 0 && numerator > 0) { numerator = -numerator; } if (integer < 0 && numerator == 0) { integer = -integer; } if (numerator >= denominator) { integer += numerator / denominator; numerator = numerator % denominator; } int gcd = gcd(numerator, denominator); numerator /= gcd; denominator /= gcd; } private int gcd(int a, int b) { if (b == 0) { return a; } return gcd(b, a % b); } public Fraction add(Fraction other) { int newNumerator = this.numerator * other.denominator + other.numerator * this.denominator; int newDenominator = this.denominator * other.denominator; int newInteger = this.integer + other.integer; return new Fraction(newInteger, newNumerator, newDenominator); } public String toString() { if (integer == 0 && numerator == 0) { return "0"; } String result = ""; if (integer != 0) { result += integer; if (numerator != 0) { result += "_"; } } if (numerator != 0) { result += numerator + "/" + denominator; } return result; } } ``` 这个带分数类实现了以下功能: - 构造函数可以根据整数部分、分子和分母创建一个带分数对象。 - simplify() 方法可以将带分数对象化简,如将负号移到分子上、将整数部分和真分数部分合并、将分数化简等。 - add() 方法可以将两个带分数对象相加,返回一个新的带分数对象。 - toString() 方法可以将带分数对象转换为字符串形式。 这个类实现了带分数的加法操作,可以参考这个类来实现其他的运算操作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值