千锤百炼之每日算法(一)

 

目录

题外话

正题

第一题

第一题思路

第一题代码详解

第二题

第二题思路

动态规划

解法一

解法一代码详解

解法二

第三题

第三题思路

第三题代码详解

小结


题外话

从今天开始,每天都会更新算法题,一天就三道题

大家最好采用码形结合的方式,也就是代码和图形结合,这样理解真的很快

而且这里是写文章,不是视频,大家理解起来肯定会吃力一点

需要画图的题,我一定会给大家尽量画清楚,帮助大家理解

想学习的人,那怕全是文字,也会一点一点去琢磨

不想学习的人,哪怕是视频喂到他嘴里,他都不想去动脑思考一下

喜欢的美女帅哥们,给个三连好嘛!!!(点赞关注收藏!!!) 

正题

第一题

帮助牛牛寄快递

牛牛正在寄快递,他了解到快递在 1kg 以内的按起步价 20 元计算

超出部分按每 kg 1元计算不足 1kg 部分按 1kg计算

如果加急的话要额外付五元,请问牛牛总共要支付多少快递费

第一行输入一个单精度浮点数 a 和一个字符 b
a 表示牛牛要寄的快递的重量,b表示牛牛是否选择加急,'y' 表示加急 ,'n' 表示不加急。

第一题思路

邮费共有四种情况
1.1kg以内是否加急,总费用等于是否加急费用加上起步价
2.1kg以外是否加急,总费用等于是否加急费用加上起步价加上从超出1kg以外费用

这道题有一个方法非常关键,掌握了这个方法这道题没啥难度

Math.ceil(double d)

简单说一下,传入一个双精度浮点型,会自动向上取整,返回值是double类型

例如

double  d=15.38;

System.out.println(Math.ceil(d));

输出结果就是16.0

放到这道题当中,我们可以知道,当牛牛的邮寄重量超过1kg,而且不足1kg,就会按照1kg收费

我们都知道,要解决关键问题,一定要找到问题的关键!!

所以向上取整Math.ceil(double d)就正好解决了了这个关键问题 

第一题代码详解

public int test1()
{
    Scanner s=new Scanner(System.in);
    //输入快递重量
    float weight=s.nextFloat();
    //输入'y'和'n'表示是否加急,转换成字符串形式
    char ch=s.next().charAt(0);
    //只要邮寄就至少需要20邮费
    int money=20;
    //当重量小于等于1的时候
    if(weight<=1)
    {
        //加急就加5,不加急就加0
       money+=ch=='y'?5:0;
    }
    else {
    //当重量大于1的时候,判断是否加急,加急的话就用Math.ceil方法取最大整数,减去1kg以内,加上5,不加急就加0
       money+=ch=='y'?5+Math.ceil(weight-1):Math.ceil(weight-1);
    }

//最后返回邮寄需要支付的费用
    return money;
}

第二题

最小支付爬楼梯问题

给定一个整数数组cost,其中cost[i]是从楼梯第i 台阶向上爬需要支付的费用
 下标从0开始。一旦你支付此费用,即可选择向上爬一个或者两个台阶。
你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯。
请你计算并返回达到楼梯顶部的最低花费。

输入描述:
第一行输入一个正整数 n ,表示数组 cost 的长度。
第二行输入 n 个正整数,表示数组 cost 的值。

输出描述:
输出最低花费

示例1.

示例2.

 

第二题思路

 第二题需要一个算法思想,就像大家在解答鸡兔同笼问题,是不是需要一元二次方程?

但是这个算法思想比一元二次方程灵活许多

动态规划

动态规划一共分为五个步骤

1.状态表示

2.状态转移方程

3.初始化 

4.填表顺序

5.返回值

状态表示就是思路,找到题目给你的信息

比如鸡兔同笼,你需要知道一共笼子里几条腿?你还需要知到鸡是两条腿,兔子是四条腿没错吧?!

你找到的状态表示不一样,下面步骤也会不一样

大家可以把这道题和数学应用题联系起来

比如鸡兔同笼问题,你设x为鸡数量,y为兔数量,是一种解题方法,你设x为兔,y为鸡也是一种解题方法,

设置的未知数不一样,解题的时候写的公式也是不一样的

我们可以简单的理解状态转移方程就是鸡兔同笼问题的二元一次方程公式

你把这个公式写明白了,然后再去计算再去写答鸡有多少只,兔子有多少只

我希望大家把这道题的题目理解清楚再看题解

接下来就是这道题运用动态规划的详解

解法一

2.状态表示

以i位置为结尾,然后通过题目信息建立状态表示

dp[i]:到达i位置的最小花费

2.状态转移方程

用之前或者之后的状态,推导出dp[i]的值

由题可知我们要想到达dp[i],分两种情况

第一种:先到达i-1位置,走一步到达i位置,也就是到达i-1位置的最小花费加上cost[i-1]花费,这就是到达dp[i]花费的钱 dp[i-1]+cost[i-1]

第二种:先到达i-2位置,走两步达到i位置, 也就是到达i-2位置的最小花费加上cost[i-2]花费,这就是到达dp[i]花费的钱 dp[i-2]+cost[i-2]

我们只需要找到i-1位置到达i和i-2位置到达i位置的最小值

dp[i]=min[dp[i-1]+cost[i-1], dp[i-2]+cost[i-2]]

3.初始化

根据动态转移方程然后去初始化,先思考我们要从数组的那个下标开始走?

如果从i=0开始走,是不是会越界?从i=1开始走也会越界

所以我们要从i=2开始走

没问题吧?!

从i=2开始走的时候我们就结合动态转移公式,可以算出dp[0]和dp[1]的最小花费,也就是cost[i-1]和cost[i-2]最小值

然后继续往后走,因为前面已经算出最小值了,再往后走i=3的时候,算出的也一定是最小值

以此类推,当i走到楼顶的时候,算出来的也一定是最小值

4.填表顺序

我们需要知道从数组左边花费,然后才能知道到达数组右边的最小花费

所以是从左往右走

5.返回值

那么dp[n]就是我们到达楼梯顶部的最小花费

解法一代码详解

public int test2(int[] cost)
    {
        //保存cost元素数量,下标最大值为n-1
        int n=cost.length;


        //因为楼顶会超出cost元素数量一个,所以dp[]的元素数量为n+1,下标最大值为n,到达n说明到楼顶了
        int[] dp=new int[n+1];
        //从i=2开始,以免数组越界
        for (int i=2;i<=n;i++)
        {
            //dp[i]是到达i位置最小花费,
            // 一共有两种情况
            // 一种是到达i-1位置加上i-1位置花费
            // 一种是到达i-2位置加上i-2位置花费
            //获取上面这两种情况最小值,就是dp[i]的最小值
            dp[i]=Math.min(dp[i-1]+cost[i-1],dp[i-2]+cost[i-2]);
        }
        return dp[n];
    }

解法二

我们解法一是通过从前往后的顺序去推导出最小花费

解法二我们就从后往前推导出最小花费

和解法一略有不同

1.状态表示

以i位置为起点,通过题目信息建立状态表示

设dp[i]从i出发到达楼顶最小花费

2.状态转移方程

想知道i位置出发到达楼顶的最小花费

我们需要知道从i+1位置出发和i+2位置出发到达楼顶的最小花费

也就是dp[i]=Math.Min(dp[i+1]+cost[i],dp[i+2]+cost[i])

我们可以先让dp[i+1]和dp[i+2]比较,然后再加上cost[i]

3.初始化

我们应该从n-1位置和n-2位置往回推

根据动态转移公式考虑越界的情况,i初始值应该等于n-3

算出n-1位置出发和n-2位置出发到达楼顶的最小值

这里我们需要初始化n-1位置出发到达楼顶的最小值,也就是cost[n-1]的花费,因为n-1直接能到楼顶,所以n-1位置出发到达楼顶最小花费就是cost[n-1]

和初始化n-2位置出发到达楼顶的最小值,也就是cost[n-2],因为n-2直接能到楼顶,没必要再上一个台阶到达n-1支付再去楼顶

为什么解法一不需要初始化??因为解法一第0个台阶和第1个台阶,就是动态转移方程的cost[i-1]和cost[i-2],可以去对比一下

而解法二这里的n-1和n-2位置出发到达楼顶的最小值咱们没有赋值

然后算出n-2位置和n-3位置出发到达楼顶的最小值

以此类推,我们从后往前算出的都是最小值

那么从0和1出发到达楼顶也一定是最小值

最后只需要算出从0和1出发到达楼顶哪个是最小值即可

4.填表顺序

我们从n-1和n-2的位置往回退,所以是从右往左的顺序

5.返回值

我们最后会得到,dp[0]和dp[1],也就是从0位置出发的最小花费和从1位置出发的最小花费

最后返回它们的最小值即可

解法二代码详解

public int test3(int[] cost)
{
    //保存cost元素数量
    int n=cost.length;


    //因为我们是从n-1,n-2位置出发到达楼顶,只需要知道n-1位置花费和n-2位置花费即可
    int[] dp=new int[n];


    //初始化n-1位置出发最小花费和n-2出发的最小花费
    dp[n-1]=cost[n-1];
    dp[n-2]=cost[n-2];


    //从i=2开始,以免数组越界
    for (int i=n-3;i>=0;i--)
    {
        //dp[i]是从i位置出发到达楼顶的最小花费,
        // 一共有两种情况,一种是到达i+1位置加上i位置花费,一种是到达i+2位置加上i位置花费
        //获取上面这两种情况最小值,就是dp[i]的最小值
        dp[i]=Math.min(dp[i+1]+cost[i],dp[i+2]+cost[i]);
    }
    return Math.min(dp[0],dp[1]);

}

第三题

两个字符串在字符串数组中最短距离问题

给定一个字符串数组strs再给定两个字符串str1和str2

返回在strs中str1与str2的最小距离
如果str1或str2为null,或不在strs中,返回-1。

示例

第三题思路

1.判断str1和str2是否为空,有一个为空直接返回-1

2.遍历str

3.我们需要创建两个变量记录分别记录str1和str2最近一次出现的位置

4.还需要创建一个最小距离变量,当str1出现之后,立刻和str2进行距离比较

5.如果遍历str结束最小距离值仍然没有改变,那就说明str中不包含str1或者str2返回-1

第三题代码详解

public int test4(String[] str,String str1,String str2)
{
    //创建min为最小距离值,str1和str2距离值最大也不会等于str数组长度
    int min=str.length;
    //s1记录str1最近出现在str数组中的下标,s2记录str1最近出现在str数组中的下标
    int s1=-1;
    int s2=-1;
    //如果str1或者str2为空直接返回-1
    if (str1==null||str2==null)
    {
        return -1;
    }
    //当他们都不为空
    else {
        //遍历str数组
        for (int i=0;i<str.length;i++)
        {
            //如果找到str1
            if (str[i].equals(str1))
            {
                //需要判断s2是否等于-1,等于-1说明str2还没有找到,min仍然为str.length
                //找到了则进行比较,此时i绝对大于s2,i-s2就是他们的距离
                //判断距离最小值
                min=Math.min(min,s2==-1?min:i-s2);
                //记录str1在str中的位置
                s1=i;
            }
            //如果找到str2
            if (str[i].equals(str2))
            {
                //需要判断s1是否等于-1,等于-1说明str1还没有找到,min仍然为str.length
                //找到了则进行比较,此时i绝对大于s1,i-s1就是他们的距离
                //判断距离最小值
                min=Math.min(min,s1==-1?min:i-s1);
                //记录str2在str中的位置
                s2=i;
            }
        }
        //最后返回min判断min是否等于数组长度,等于就说明没有找到返回-1,不等于则找到了返回min
        return min==str.length?-1:min;
    }

}

小结

到现在我还没有吃饭,饿了饿了,先去吃饭了

大家有问题再评论区留言

麻烦大家三连一下!!!(点赞关注收藏!!!)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值