蓝桥杯练习-3.10

蓝桥杯练习-3.10

代码练习

• 龟兔赛跑

在这里插入图片描述

思路

本题的关键是当兔子领先乌龟t米时,我们并不需要像传统一样的,让兔子走的路程停止,而乌龟继续行走s秒。而是可以让兔子退后v1×s的路程,而当乌龟在s秒内行走的时候,兔子相当于在补之前后退的距离,当s秒过去了,乌龟走了s秒,而兔子刚好回到了原位,这就相当于兔子在这段时间内休息。

代码实现

#include <iostream>
using namespace std;
int main()
{
    int v1, v2, t, s, l;//兔子的速度是v1,乌龟的速度是v2
    int ans1 = 0;//兔子当前走过的路程
    int ans2 = 0;//乌龟当前走过的路程
    cin >> v1 >> v2 >> t >> s >> l;
    int i = 0;
    while(ans1 < l && ans2 < l)
    {
        ans1 += v1;
        ans2 += v2;
        i++;
        if (ans1 == l || ans2 == l)
            break;
        while(ans1 - ans2 >= t)
        {
            ans1 -= v1 * s;
        }
    }
    if (ans1 > ans2)
    {
        cout << "R" << endl << i;
    }
    else if (ans2 > ans1)
    {
        cout << "T" << endl << i;
    }
    else
        cout << "D" << endl << i;
    return 0;
}

❕注意:我刚开始是用传统的方法来实现代码的,发现有些测试案例过不了,原因是,乌龟可能在兔子休息的s秒内就已经到达了终点,而我们还在让时间继续,乌龟仍然在动,这就导致结果出错

改进:

while(ans1 < l && ans2 < l)
    {
        if (ans1 == l || ans2 == l)
            break;
        ans1 += v1;
        ans2 += v2;
        i++;
        while(ans1 - ans2 >= t)
        {
            for (int j = 1; j <= s; j++)
            {
                ans2 += v2;
                t++;
                if (ans2 == l)
                    break;
            }
        }
    }
• C 乘积尾零(第九届蓝桥杯)

Description

如下的10行数据,每行有10个整数,请你求出它们的乘积的末尾有多少个零?

5650 4542 3554 473 946 4114 3871 9073 90 4329

2758 7949 6113 5659 5245 7432 3051 4434 6704 3594

9937 1173 6866 3397 4759 7557 3070 2287 1453 9899

1486 5722 3135 1170 4014 5510 5120 729 2880 9019

2049 698 4582 4346 4427 646 9742 7340 1230 7683

5693 7015 6887 7381 4172 4341 2909 2027 7355 5649

6701 6645 1671 5978 2704 9926 295 3125 3878 6785

2066 4247 4800 1578 6652 4616 1113 6205 3264 2915

3966 5291 2904 1285 2193 1428 2265 8730 9436 7074

689 5510 8243 6114 337 4096 8199 7313 3685 211

注意:需要提交的是一个整数,表示末尾零的个数。不要填写任何多余内容。

Input

见上文描述。

思路

本题问的是最后的乘积末尾有多少个0,我们知道,乘以10可以得到1个零,而10是有2乘5得到的,所以我们只要算出上述数据可以拆分成多少个2和5(有多少个2和5的因子),然后求出其中的最小值,即为最后有多少个零。

#include <iostream>
using namespace std;
int a[105];
int num2 = 0;
int num5 = 0;
int result;
int f2(int n)
{
    int num = 0;
    if(n % 2 == 0)
    {
        while (n % 2 == 0)
        {
            num++;
            n /= 2;
        }
    return num;
    }
    else return 0;
}
int f5(int n)
{
    int num = 0;
    if(n % 5 == 0)
    {
        while (n % 5 == 0)
        {
            num++;
            n /= 5;
        }
    return num;
    }
    else return 0;
}
int main()
{
    for (int i = 1; i <= 100; i++)
    {
        cin >> a[i];
    }
    for (int i = 1; i <= 100; i++)
    {
        num2 += f2(a[i]);
        num5 += f5(a[i]);
    }
    result = min(num2, num5);
    cout << result << endl;
    return 0;
}
• D 测试次数

Description

标题:测试次数

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

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

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

如果手机从第7层扔下去没摔坏,但第8层摔坏了,则手机耐摔指数=7。

特别地,如果手机从第1层扔下去就坏了,则耐摔指数=0。

如果到了塔的最高层第n层扔没摔坏,则耐摔指数=n

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

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

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

注意:需要填写的是一个整数,不要填写任何多余内容。

Input

见上文描述。

Output

代码实现

#include <iostream>
using namespace std;
int dp[5][10005];//dp[i][j]表示i部手机测试j层楼的最多测试次数
void f(int phone, int floor)
{
    for (int j = 1; j <= floor; j++)
    {
        dp[1][j] = j;//先初始化一部手机,在测试j层楼的最多测试次数,当然一部手机最多测试次数就是从最开始一楼一楼去测,所以次数是楼层高度
    }
    for (int i = 2; i <= phone; i++)//然后从第二部手机开始,循环
    {
        for (int j = 1; j <= floor; j++)//因为是动态规划,所以我们要知道楼层为j时,每取k为不同值的所有情况,这样子问题的答案知道,最终答案也可以推出
        {
            for (int k = 1; k < j; k++)//从第k层摔下
            {
                dp[i][j] = min(dp[i][j], max(dp[i - 1][k - 1], dp[i][j - k]) + 1);
            }
        }
    }
}
int main()
{
    f(3, 1000);
    cout << dp[3][1000] << endl;
    return 0;
}

观看博客

一道关于扔球/扔鸡蛋/摔手机的DP问题

以下文字和思路大部分来自以上博客已经其他的。

题目

一幢 100 层的大楼,给你两个鸡蛋. 如果在第 n 层扔下鸡蛋,鸡蛋不碎,那么从前 n-1 层扔鸡蛋都不碎.
这两只鸡蛋一模一样,不碎的话可以扔无数次. 已知鸡蛋在0层扔不会碎.
提出一个策略, 要保证能测出鸡蛋恰好会碎的楼层, 并使此策略在最坏情况下所扔次数最少.

❕注意:“最佳策略”,“最坏运气”,意思应该是把3部手机都摔坏并且是最后一次才测出来耐摔指数的最小次数。这是一个最优解的问题

最佳策略代表整体尝试放手机在各个层数的最小次数,最坏运气代表单次放置手机的最大值。

思路

我们不妨这样思考,假设问题存在最优解,这个最优解的最坏情况尝试次数是x次。那么,我们第一次扔鸡蛋恰恰是从第x层开始扔。要想尽量楼层跨度大一些,又要保证不超过假设的尝试次数x,那么第一次扔鸡蛋的最优选择就是第x层。如果第一次扔鸡蛋没有碎,我们的尝试次数消耗了一次,问题就转化成了两个🥚在100 - x层楼往下扔,要求尝试次数不得超过x - 1次。由于尝试次数上限变成了x - 1次,所以第二次尝试的楼层楼层跨度也是x - 1,第三层楼层跨度是x - 2,可得方程
x + ( x − 1 ) + ( x − 2 ) + . . . . + 1 = 100 x + (x - 1) + (x - 2) + .... + 1 = 100 x+(x1)+(x2)+....+1=100
最终x向上取整,得到 x = 14

因此,最优解在最坏情况的尝试次数是14次。

那么这个问题可以推广到一般情况

一般情况

给定N层楼和i个球。用i个球检测在这N层楼中的某一层t球扔下楼时不碎,而在t+1层球扔下楼时会碎,则t层称为最高安全层。求用i个球一定可以检测N层楼的最高安全层的最少扔球次数。(注:球不碎还可以再用)

思路

dp[i] [j]表示i部手机j层楼的最少测试次数

现在给定i个球,j层楼,假设我们在某一层k扔一个球,那么就会出现两种情况:

(1)如果球碎了,那我们需要往第k层楼以下的楼层去测试。在第k层我们测试了,所以次数要计数1;还需往下的楼层作测试,这时还有i-1个球去测试k-1层楼,即需计次数 dp[i-1] [k-1]。综上,在情况(1)下,测试次数为1+dp[i-1] [k-1]。

(2)如果球不碎,那我们需要往第k层楼以上的楼层去测试。在第k层我们测试了,所以次数要计数1;还需往上的楼层作测试,这时还有i个球去测试j-k层楼,即需计次数 dp[i] [j-k]。综上,在情况(2)下,测试次数为1+dp[i] [j-k]。

在(1)(2)两种情况中,我们需要取两者的最大值,因为我们需要保证一定可以测试出最高安全楼层。由上可见,k是一个变量,取值范围为0<=k<=j。这表明我们需要遍历0j,每次求出(1),(2)两种情况的最大值,为了简便,我们将0j遍历所得的最大值记为{m0,m1,m2…mj},我们还需要在这些值(这些值都是可以保证一定能测试出最高安全楼层)中选出最小值,因为我们需要的是最少的次数。

所以得到状态转移方程:
d p [ i ] [ j ] = m i n ( d p [ i ] [ j ] , m a x ( d p [ i − 1 ] [ k − 1 ] , d p [ i ] [ j − k ] ) + 1 ) dp[i][j] = min(dp[i][j], max(dp[i - 1][k - 1], dp[i][j - k]) + 1) dp[i][j]=min(dp[i][j],max(dp[i1][k1],dp[i][jk])+1)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值