本次分享牛客网的三道算法题的题解思路和源码:牛牛的快递、最小花费爬楼梯、数组中两个字符串的最小距离
目录
一.BC64 牛牛的快递
题目描述:
题目链接:牛牛的快递_牛客题霸_牛客网
▐ 题解
对于这道题目,解决起来主要有俩个点
- 不足1kg要算作1kg
- 加急快递要多收5块钱
不足1kg要算作1kg的逻辑其实就是向上取整,有俩种方法实现向上取整,第一种方法:将输入的浮点型强转为整形,则会自动向下取整,在此基础上再+1,那么就完成了向上取整的操作;第二章方法:直接调用Math类的库函数 ceil() 方法,即可自动获取到向上转型的结果,但这对jdk的API的了解则有一定的要求
对于加急还是不加急,这很好判断,使用一个 if 判断即可
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
double a = in.nextDouble();
int count = 0;
char b = in.next().charAt(0);//in.next()是取一个字符串,用charAt取第一个字符
if( a <= 1) {
count = 20;
}else {
count = 20 + (int)Math.ceil(a-1);
// int over = (int)(a - 1); //超出部分,向下取整
// if ( a-over == 1) {//超出部分为整数
// count = 20 + over;
// }else {//超出部分为小数
// count = 20 + (over + 1);
// }
}
if(b == 'y') {
count += 5;
}
System.out.println(count);
}
}
二.DP4 最小花费爬楼梯
题目描述:
题目链接:最小花费爬楼梯_牛客题霸_牛客网
▐ 题解
阅读完题目后,就可以知道这是一道很经典的动态规划题目,从后往前思考,由于每一次可以跳一级或者俩级台阶,那么到达最后一个台阶就只能从倒数第二个台阶或者倒数第三个台阶开始跳,因此只要求这俩种方式的花费的最小值即可得到要跳到最后一步所需要的花费,从后往前类推其实每一步都是这样,使用cost数组来记录每一步向上爬所需要的花费,使用dp数组来记录总花费。
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = in.nextInt();
int[] cost = new int[n];
int[] dp = new int[n + 1];
for (int i = 0; i < n; i++) {
cost[i] = in.nextInt();
}
dp[0] = 0;
dp[1] = 0;
for (int i = 2; i <= n; i++) {
dp[i] = Math.min(dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i - 2]);
}
System.out.println(dp[n]);
}
}
三.数组中两个字符串的最小距离
题目描述:
▐ 题解
首先是理解题目,第一行输入代表数组长度,第二含代表我们要找的俩个字符串,其余的代表数组的内容。
假如用传统的暴力的解法,那就是俩轮遍历,分别用俩个指针来控制俩层遍历的指向,挨个挨个遍历可行解的距离,最后再求所有距离之中的最小值。算法时间复杂度为O的平方。
通过贪心的思想,我们可以优化传统的暴力解法,还是用俩个指针来记录我们要找的俩个字符串,但是不一样的是,我们只用一次循环。首先将 pre1 和 pre2 置为 -1 代表俩个指针刚开始没有指向有效元素,pre1 指向所找的第一个字符串,pre2 指向所找的第二个字符串;使用一个非常大的数来初始化 ret,当我们找到更短距离后就更新 ret。
一旦开始循环遍历,我们就依次判断当前元素是否为我们想要找的元素:
- 如果当前找到了字符串1,并且pre2已经找到了字符串2,那么就计算俩个字符串之间的距离,并且与ret比较,得到俩个距离中最短的那个;如果pre2还没有找到字符串2,那我们就无法计算结果,就继续往后走,但是不论当前pre2有没有找到字符串2,我们都应该更新pre1,因为字符串1的位置已经更新了。
- 如果找到的是字符串2,并且pre1已经找到了字符串1,那么就计算俩个字符串之间的距离,并且将最小值交给ret进行记录;如果pre1还没有找到字符串1,那我们就无法进行计算,故而继续往后走,但不论当前pre1有没有找到字符串1,我们都应该更新pre2,因为字符串2已经被找到了。
以上便是算法原理,你可能会思考这为什么就能找到最优解呢?我们可以通过一个情景来验证一下算法的正确性:
假如pre1先找到了字符串1,然后继续往后走,当pre2第一次找到字符串2的时候,我们就得到了第一个距离。此时此刻,对于pre1来说,pre2找到的字符串2就是离他最近的那个字符串2,后面的所有字符串2都比此刻的距离远;但是对于pre2来说,此时此刻可能还存在更短距离,因为在pre1到pre2这个区间内,我们可以保证一定不存在字符串2,但不能保证不存在字符串1,即下图这样的情况。那么此时此刻,我们固定住pre2的指向,让pre1继续往后走,就会最终找到这个区间内的最优解。
当pre1一直走,走出这个区间后他还继续走,当走出上述这个区间后,pre1又找到了一个字符串1,那么此时就会形成下图这样的情况,此时我们按照刚才的方法固定住pre1,让pre2继续往后走,那么就能找到这个区间内的最短距离
如此反复,整个数组也只可能出现上述俩种情况,即pre1在pre2前面或者pre2在pre1前面,既然对于这俩种方式我们都能找到各自区间的最优解,那么合起来,我们就能得到所有情况下的最优解
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = in.nextInt();
String s1 = in.next();
String s2 = in.next();
int pre1 = -1;
int pre2 = -1;
int ret = 0x3f3f3f3f;
for(int i=0; i < n; i++) {
String s = in.next();
if(s.equals(s1)) {//遇见的是字符串1,则去找前面的字符串2
if(pre2 != -1) {//前面有字符串2
ret = Math.min(ret, i - pre2);//计算距离
}
pre1 = i;//不管前面有没有字符串2都要更新位置
}else if(s.equals(s2)) {//遇见的是字符串2,则去找前面的字符串1
if(pre1 != -1) {
ret = Math.min(ret, i -pre1);//计算距离
}
pre2 = i;//不管前面有没有字符串1都要更新位置
}
}
System.out.println(ret == 0x3f3f3f3f ? -1 : ret);
}
}
本次的分享就到此为止了,希望我的分享能给您带来帮助,创作不易也欢迎大家三连支持,你们的点赞就是博主更新最大的动力!
如有不同意见,欢迎评论区积极讨论交流,让我们一起学习进步!
有相关问题也可以私信博主,评论区和私信都会认真查看的,我们下次再见