训练-我的基础算法刷题之路(四)

本篇博客旨在整理记录自己刷的一些蓝桥杯训练题的思路、代码以及注解,同时希望可给小伙伴一些帮助。本人也是算法小白,水平有限,如果文章中有什么错误之处,希望小伙伴们可以在评论区指出来,共勉 。

文章目录

    • 训练题目
      • 1、预备爷的悲剧
      • 2、饮料换购
      • 3、优秀的拆分
      • 4、日期问题
      • 5、回文日期
      • 6、印章
      • 7、拿金币
      • 8、24点
    • 最后

训练题目

1、预备爷的悲剧

题目:语预备爷gzp是个逗(tu)比(hao),为了在即将到来的英语的quiz中不挂科,gzp废寝忘食复习英语附录单词表,俨然一场人间悲剧。不过上天有好生之德,上帝扔给了gzp一张纸,上面记载了将要考到的单词。不过gzp是个逗比,之前复习的东西全忘记了,所以他又要再来一次复习。不过已经知道了要考的单词,所以不需要复习单词表的所有页数。因此,现在需要你帮助他求出有多少页纸需要复习。他会告诉你每个单词会在哪几页出现,并且告诉你要考哪些单词,你只要告诉他答案就可以了。由于一个单词会出现在不同页上,只需要复习在最前面一页上的就可以了。

输入格式:

  第一行一个整数n,表示单词附录有n个单词。接下来n行每行一个小写字母组成的单词和一个整数,表示某一个单词和它所在的页数。接下来是一行整数m,表示要考m个单词,接下来m行小写字母组成的单词,表示要考到的单词。

输出格式:

 一个数,表示需要复习的页数

输入输出样例:

输入:

5
ab 1
ac 2
ab 2
ac 3
c 3
3
ab
ac
c

输出:

3

数据规模和约定: 0 <= n,m <= 100000,单词长度 <= 10。

解题代码:

import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner s = new Scanner(System.in);
        // 创建 Set 集合用来保存需要复习的页数
        Set<Integer> set = new HashSet<>();
        // 单词表中单词的录入
        int n = s.nextInt();
        Map<String, Integer> map = new HashMap<>();
        for (int i = 0;i < n; i++) {
            // 录入单词以及所在的页数
            String key = s.next();
            int value = s.nextInt();
            // 这里判断 key 是否出现,如果没出现过直接保存到单词表,如果出现过判断 value 值,保存小的那个
            // containsKey() 方法检查 hashMap 中是否存在指定的 key 对应的映射关系。存在返回true
            if (!map.containsKey(key)) {
                map.put(key, value);
            } else {
                if (map.get(key)>value) {
                    map.put(key, value);
                }
            }
        }
        // 录入需要考试的单词
        int m = s.nextInt();
        String[] str = new String[m];
        for (int i = 0; i < m; i++) {
            // 输入要考试的单词
            str[i] = s.next();
            // 判断这个单词是否在单词表中
            if (map.containsKey(str[i])) {
                // 如果在单词表中则把单词对应的 value 值保存到 Set 中去
                set.add(map.get(str[i]));
            }
        }
        // 调度 size 方法得到需要复习的页数
        System.out.println(set.size());
    }
}

2、饮料换购

题目:乐羊羊饮料厂正在举办一次促销优惠活动。乐羊羊 C 型饮料,凭 3 个瓶盖可以再换一瓶 C 型饮料,并且可以一直循环下去(但不允许暂借或赊账)。

请你计算一下,如果小明不浪费瓶盖,尽量地参加活动,那么,对于他初始买入的 n 瓶饮料,最后他一共能喝到多少瓶饮料。

输入格式:

 输入一个整数 n(0 < n < 1000),表示开始购买的饮料数量。

输出格式:

 输出一个整数,表示实际得到的饮料数

输入输出样例:

输入

100

输出

149

运行限制:

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

解题代码:

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner s = new Scanner(System.in);
        int n = s.nextInt();
        int num = n;
        while (n/3 != 0){
            num += n / 3;
            n = (n%3) + (n/3);
        }
        System.out.println(num);
    }
}

3、优秀的拆分

题目:一般来说,一个正整数可以拆分成若干个正整数的和。

例如,1=1,10=1+2+3+41=1,10=1+2+3+4 等。对于正整数 n 的一种特定拆分,我们称它为“优秀的”,当且仅当在这种拆分下,n 被分解为了若干个不同的 22 的正整数次幂。注意,一个数 x 能被表示成 22 的正整数次幂,当且仅当x 能通过正整数个 22 相乘在一起得到。

例如,10=8+2=23+2110=8+2=23+21是一个优秀的拆分。但是,7=4+2+1=22+21+207=4+2+1=22+21+20 就不是一个优秀的拆分,因为 11 不是 22 的正整数次幂。

现在,给定正整数n,你需要判断这个数的所有拆分中,是否存在优秀的拆分。若存在,请你给出具体的拆分方案。

输入格式:

输入只有一行,一个整数 n (1≤n≤107),代表需要判断的数。

输出格式:

如果这个数的所有拆分中,存在优秀的拆分。那么,你需要从大到小输出这个拆分中的每一个数,相邻两个数之间用一个空格隔开。可以证明,在规定了拆分数字的顺序后,该拆分方案是唯一的。

若不存在优秀的拆分,输出 -1

输入输出样例:

输入

6

输出

4 2

运行限制:

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

解题代码:

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner s = new Scanner(System.in);
        int n = s.nextInt();
        // 奇数直接输出-1即可结束
        if (n % 2 != 0) {
            System.out.println(-1);
            return ;
        }

        // 对于偶数的情况,找到2的k次方小于n,且k最大
        while (n != 0){
            n = f(n);
        }
    }

    private static int f(int n) {
        int res = 1;
        while (Math.pow(2, res) <= n) {
            res++;
        }
        System.out.println((int)Math.pow(2, res-1) + " ");
        return n - (int)Math.pow(2, res - 1);
    }
}

4、日期问题

题目:小明正在整理一批历史文献。这些历史文献中出现了很多日期。小明知道这些日期都在 1960 年 1 月 1 日至 2059 年 12 月 31 日。令小明头疼的是,这些日期采用的格式非常不统一,有采用年/月/日的,有采用月/日/年的,还有采用日/月/年的。

更加麻烦的是,年份也都省略了前两位,使得文献上的一个日期,存在很多可能的日期与其对应。

比如 02/03/04,可能是 2002 年 03 月 04 日、2004 年 02 月 03 日或 2004 年 03 月 02 日。

给出一个文献上的日期,你能帮助小明判断有哪些可能的日期对其对应吗?

输入格式:

一个日期,格式是 “AA/BB/CC" (0≤A,B,C≤9)。

输出格式:

输出若干个不相同的日期,每个日期一行,格式是 “yyyy−MM−dd”。多个日期按从早到晚排列。

输入输出样例:

输入

02/03/04

输出

2002-03-04
2004-02-03
2004-03-02

运行限制:

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

解题代码:

import java.util.*;

public class Main {
    public static void one(int y,int m,int d){
        int year=y+(y<60?2000:1900);
        month[1]=((year%4==0&&year%100!=0)||year%400==0)?29:28;
        if(m<13&&m>0&&d>0&&d<=month[m-1]){
            System.out.println(year+"-"+(m<10?"0"+m:m)+"-"+(d<10?"0"+d:d));
        }
    }
    public static int month[]={31,28,31,30,31,30,31,31,30,31,30,31};
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        String s=sc.next();
        String ss[]=s.split("/");
        int arr[]=new int[3];
        for(int i=0;i<3;i++){
            arr[i]=Integer.valueOf(ss[i]);
        }
        one(arr[0],arr[1],arr[2]);
        one(arr[2],arr[0],arr[1]);
        one(arr[2],arr[1],arr[0]);
    }
}

5、回文日期

题目:2020 年春节期间,有一个特殊的日期引起了大家的注意:2020 年 2 月 2 日。因为如果将这个日期按 “yyyymmdd” 的格式写成一个 8 位数是 20200202,恰好是一个回文数。我们称这样的日期是回文日期。

有人表示 20200202 是 “千年一遇” 的特殊日子。对此小明很不认同,因为不到 2 年之后就是下一个回文日期:20211202 即 2021 年 12 月 2 日。

也有人表示 20200202 并不仅仅是一个回文日期,还是一个 ABABBABA 型的回文日期。对此小明也不认同,因为大约 100 年后就能遇到下一个 ABABBABA 型的回文日期:21211212 即 2121 年 12 月 12 日。算不上 “千年一遇”,顶多算 “千年两遇”。

给定一个 8 位数的日期,请你计算该日期之后下一个回文日期和下一个 ABABBABA 型的回文日期各是哪一天。

输入格式:

 输入包含一个八位整数N,表示日期。

对于所有评测用例,10000101 ≤ N ≤ 89991231,保证N 是一个合法日期的 8 位数表示。

输出格式:

 输出两行,每行 1 个八位数。第一行表示下一个回文日期,第二行表示下一个 ABABBABA 型的回文日期。

输入输出样例:

输入

20200202

输出

20211202
21211212

运行限制:

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

解题代码:

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String date = sc.next();

        // 用于判断第一个回文是否已经输出
        // 因为在第一个回文和第一个ABAB之间可能还存在若干个回文,这些回文我们不输出
        int key = 0;

        // 因为不知道下一个回文日期,所以定义一个死循环,遇到回文日期再break
        while (true) {
            while (true) {
                // 日期自加
                date = AddDate(date);

                // 输出下一个回文
                if (isHuiWen(date)) {
                    if (key == 0) {
                        System.out.println(date);
                        key++;
                        break;
                    } else {
                        break;
                    }
                }
            }

            if (isABAB(date)) {
                System.out.println(date);
                break;
            }
        }
    }

    // 判断回文
    public static boolean isHuiWen(String date) {
        // 回文日期,就是日期反转后,与原来的日期相等
        return date.equals(new StringBuffer(date).reverse().toString());
    }

    // 判断ABAB -- 在回文日期的基础上判断,节约时间
    public static boolean isABAB(String date) {
        // 转换成字符数组
        char[] c = date.toCharArray();
        // ABAB型,第一个字符和第三个字符相等,第二个字符与第四个字符也相等
        if(c[0] == c[2] && c[1] == c[3]) {
            return true;
        }
        return false;
    }

    // 日期自加
    public static String AddDate(String date) {
        // 转换成整型
        int d = Integer.parseInt(date);
        // 分离年月日
        int year = d / 10000;int month = d% 10000 / 100; int day = d % 100;

        int[] arr = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

        // 判断平年闰年
        if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) {
            arr[1] = 29; // 闰年二月有29天
        } else {
            arr[1] = 28; // 平年二月有28天
        }

        day++;
        if (day > arr[month - 1]) {
            month++;
            day = 1;
        }
        if (month > 12) {
            year++;
            month = 1;
        }
        return year*10000+month*100+day + "";
    }
}

6、印章

题目:共有n种图案的印章,每种图案的出现概率相同。小A买了m张印章,求小A集齐n种印章的概率。

输入格式:

 一行两个正整数n和m

输出格式:

 一个实数P表示答案,保留4位小数。

输入输出样例:

输入

2 3

输出

0.7500

数据规模和约定: 1 ≤ n,m ≤ 20

解题代码:

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner s = new Scanner(System.in);
        int n = s.nextInt(),m = s.nextInt();
        // 设置初始化
        double arr[][] = new double[m+1][n+1];
        double p = 1.0/n;
        for (int i = 1; i <= m; i++) {
            for (int j = 1; j <= n; j++) {
                if (i < j) {
                    // 当 图案数量 > 购买印章,集齐的概率为 0
                    arr[i][j] = 0;
                }
                if (j == 1) {
                    // 当 图案数量为 1 时,集齐的概率为购买印章数量的次方
                    arr[i][j] = Math.pow(p, i-1);
                } else {
                    // DP 的状态方程
                    arr[i][j] = arr[i-1][j-1]*(n-j+1)*p + arr[i-1][j]*(j*p);
                }
            }
        }
        System.out.printf("%.4f",arr[m][n]);
    }
}

7、拿金币

题目:有一个N x N的方格,每一个格子都有一些金币,只要站在格子里就能拿到里面的金币。你站在最左上角的格子里,每次可以从一个格子走到它右边或下边的格子里。请问如何走才能拿到最多的金币。

输入格式:

 第一行输入一个正整数n。
 以下n行描述该方格。金币数保证是不超过1000的正整数。

输出格式:

 最多能拿金币数量。

输入输出样例:

输入

3
1 3 3
2 2 2
3 1 2

输出

11

数据规模和约定: n <= 1000

解题代码:

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner s = new Scanner(System.in);
        int n = s.nextInt();
        int a[][] = new int[n][n];
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                a[i][j] = s.nextInt(); // a数组用来存放每个格子的金币数
            }
        }
        int dp[][] = new int[n][n];
        dp[0][0] = a[0][0]; // 第一个格子的最大值就是第一个格子的金币数
        for (int i = 1; i < n; i++) {
            dp[0][i] = dp[0][i-1] + a[0][i]; // 第一行因为每个格子的上面没有格子,所有只能加上左边格子的金币总数量
            dp[i][0] = dp[i-1][0] + a[i][0]; // 第一列因为每个格子的左边没有格子,所以只能加上上面格子的金币总数量
        }
        for (int i = 1; i < n; i++) {
            for (int j = 1; j < n; j++) {
                dp[i][j] = Math.max(dp[i][j-1], dp[i-1][j]) + a[i][j]; // 加上上面格子和左边格子更大的金币数
            }
        }
        System.out.println(dp[n-1][n-1]); // 最后一个即是n*n个格子的最大值
    }
}

8、24点

题目:24点游戏是一个非常有意思的游戏,很流行,玩法很简单:给你4张牌,每张牌上有数字(其中A代表1,J代表11,Q代表12,K代表13),你可以利用数学中的加、减、乘、除以及括号想办法得到24,例如:
 ((A*K)-J)Q等价于((113)-11)*12=24
 加减乘不用多说了,但除法必须满足能整除才能除!这样有一些是得不到24点的,所以这里只要求求出不超过24的最大值。

输入格式:

 输入第一行N(1<=N<=5)表示有N组测试数据。每组测试数据输入4行,每行一个整数(1到13)表示牌值。

输出格式:

 每组测试数据输出一个整数,表示所能得到的最大的不超过24的值。

输入输出样例:

输入

3
3
3
3
3
1
1
1
1
12
5
13
1

输出

24
4
21

运行限制:

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

解题代码:

import java.util.Scanner;

public class Demo04 {

    static int N,vis[],arr[],max,pos;
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        //表示有N组数据,每组数据输入4行
        N = sc.nextInt();
        int[][] num = new int[N][4];
        vis = new int[4];
        arr = new int[4];
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < 4; j++) {
                num[i][j] = sc.nextInt();
            }
        }
        for (int i = 0; i < N; i++) {
            max = 0;
            Dfs(1,num[i]);
            System.out.println(max);
        }
    }

    static int VF(int a,int b,int h){
        switch (h){
            case 1:
                return a+b;
            case 2:
                return a-b;
            case 3:
                return a*b;
        }
        if (b == 0){
            return 1000;
        }
        if(a % b != 0){
            return 1000;
        }
        return a/b;
    }

    static void Dfs(int step, int[] num) {
        for (int i = 0; i < 4; i++) {
            if (vis[i] == 0){
                vis[i] = 1;
                arr[pos++] = num[i];
                Dfs(step+1,num);
                vis[i] = 0;
                arr[--pos] = 0;
            }
        }
        if (step > 4){
            for (int i = 1; i <= 4; i++) {
                for (int j = 1; j <= 4; j++) {
                    for (int k = 1; k <= 4; k++) {
                        int tp1,tp2,tp3;
                        tp1 = VF(arr[0],arr[1],i);
                        tp2 = VF(tp1,arr[2],j);
                        tp3 = VF(tp2,arr[3],k);
                        if(tp3 <= 24) max = Math.max(max, tp3);
                        tp1 = VF(arr[1],arr[2],j);
                        tp2 = VF(arr[0],tp1,i);
                        tp3 = VF(tp2,arr[3],k);
                        if(tp3 <= 24) max = Math.max(max, tp3);
                        tp1 = VF(arr[1],arr[2],j);
                        tp2 = VF(tp1,arr[3],k);
                        tp3 = VF(arr[0],tp2,i);
                        if(tp3 <= 24) max = Math.max(max, tp3);
                        tp1 = VF(arr[0],arr[1],i);
                        tp2 = VF(arr[2],arr[3],k);
                        tp3 = VF(tp1,tp2,j);
                        if(tp3 <= 24) max = Math.max(max, tp3);
                        tp1 = VF(arr[2],arr[3],k);
                        tp2 = VF(arr[1],tp1,j);
                        tp3 = VF(arr[0],tp2,i);
                        if(tp3 <= 24) max = Math.max(max, tp3);
                    }
                }
            }
        }
    }
}

最后

对各位小伙伴有帮助的话,希望可以点赞❤️+收藏⭐,谢谢各位大佬~~

你可能感兴趣的:(算法刷题,算法,java,蓝桥杯,后端,数据结构)