【2020年蓝桥杯Java-B组省赛(10月第二场)题解】

一、门牌制作(暴力)

小蓝要为一条街的住户制作门牌号。
这条街一共有 2020 位住户,门牌号从 1 到 2020 编号。
小蓝制作门牌的方法是先制作 0 到 9 这几个数字字符,最后根据需要将字
符粘贴到门牌上,例如门牌 1017 需要依次粘贴字符 1、0、1、7,即需要 1 个字符 0,2 个字符 1,1 个字符 7。

请问要制作所有的 1 到 2020 号门牌,总共需要多少个字符 2?
public class Main {
    public static void main(String[] args) {
        int cnt = 0;
        for (int i = 1; i <= 2020; i++) {
            int tmp = i;
            while (tmp != 0) {
                if (tmp % 10 == 2) {
                    cnt++;
                }
                tmp /= 10;
            }
        }
        System.out.println(cnt);
    }
}

答案:624

二、寻找2020(模拟)

小蓝有一个数字矩阵,里面只包含数字 0 和 2。小蓝很喜欢 2020,他想找
到这个数字矩阵中有多少个 2020 。
小蓝只关注三种构成 2020 的方式:

  • 同一行里面连续四个字符从左到右构成 2020。
  • 同一列里面连续四个字符从上到下构成 2020。
  • 在一条从左上到右下的斜线上连续四个字符,从左上到右下构成 2020。

例如,对于下面的矩阵:

220000
000000
002202
000000
000022
002020

一共有 5 个 2020。其中 1 个是在同一行里的,1 个是在同一列里的,3 个是斜线上的。

小蓝的矩阵比上面的矩阵要大,由于太大了,他只好将这个矩阵放在了一个文件里面,在试题目录下有一个文件 2020.txt,里面给出了小蓝的矩阵。

请帮助小蓝确定在他的矩阵中有多少个 2020。

没有2020.txt文件,就直接拿示例写的,思路是一样的。

import java.util.*;

public class Main {
    public static void main(String[] args) {
        char[][] map = new char[6][6];
        Scanner scan = new Scanner(System.in);
        for (int i = 0; i < 6; i++) {
            map[i] = scan.nextLine().toCharArray();
        }
        int sum = 0;
        // 先找行
        for (int i = 0; i < 6; i++) {
            for (int j = 0; j < 6 - 3; j++) {
                int num = (map[i][j] - '0') * 1000 + (map[i][j + 1] - '0') * 100 + (map[i][j + 2] - '0') * 10 + map[i][j + 3] - '0';
                if (num == 2020) {
                    sum++;
                }
            }
        }
        // 再找列
        for (int i = 0; i < 6; i++) {
            for (int j = 0; j < 6 - 3; j++) {
                int num = (map[j][i] - '0') * 1000 + (map[j + 1][i] - '0') * 100 + (map[j + 2][i] - '0') * 10 + map[j + 3][i] - '0';
                if (num == 2020) {
                    sum++;
                }
            }
        }
        // 再斜向找
        for (int i = 0; i < 6 - 3; i++) {
            for (int j = 0; j < 6 - 3; j++) {
                int num = (map[i][j] - '0') * 1000 + (map[i + 1][j + 1] - '0') * 100 + (map[i + 2][j + 2] - '0') * 10 + map[i + 3][j + 3] - '0';
                if (num == 2020) {
                    sum++;
                }
            }
        }
        System.out.println(sum);
    }
}

三、蛇形填数(找规律)

在这里插入图片描述

package Chapter_5;
import java.util.*;

/*
1 2 6 7 15 16 28 29 45 46
3 5 8 14 17 27 30 44 47
4 9 13 18 26 31 43 48
10 12 19 25 32 42 49
11 20 24 33 41 50
21 23 34 40 51
22 35 39 52
36 38 53
37 54
55
56
 */
public class Main {
    public static void main(String[] args) {
        // 1 5 13 25 41
        // 4 8 12 16
        // 找规律,1,1 2,2 3,3
        int ans = 1;
        int gap = 4;
        for (int i = 2; i <= 20; i++) {
            ans += gap;
            gap += 4;
        }
        System.out.println(ans);
    }
}

答案:761

四、七段码(dfs搜索)

上图给出了七段码数码管的一个图示,数码管中一共有 7 段可以发光的二极管,分别标记为 a, b, c, d, e, f, g。

小蓝要选择一部分二极管(至少要有一个)发光来表达字符。在设计字符的表达时,要求所有发光的二极管是连成一片的。
在这里插入图片描述

举例:
例如:b 发光,其他二极管不发光可以用来表达一种字符。
例如:c 发光,其他二极管不发光可以用来表达一种字符。这种方案与上一行的方案可以用来表示不同的字符,尽管看上去比较相似。
例如:a, b, c, d, e 发光,f, g 不发光可以用来表达一种字符。
例如:b, f 发光,其他二极管不发光则不能用来表达一种字符,因为发光的二极管没有连成一片。

请问,小蓝可以用七段码数码管表达多少种不同的字符?

这道题,当年比赛没想出来…

注意b发光或c发光,属于不同的字符。

给一种比较蠢的做法,abcdefg,就是找它们非空的子集(组合),一共有127个,127个其实还好,就可以一个个判断是否连通(哈哈哈哈),真就数出来了答案:80。

public class Main {
    static LinkedList<LinkedList<Character>> ans = new LinkedList<>();
    static LinkedList<Character> tmp = new LinkedList<>();
    static int[] vis = new int[8];
    static char[] chars = new char[] {'a', 'b', 'c', 'd', 'e', 'f', 'g'};
    static boolean[][] graph;
    public static void main(String[] args) {
        dfs(0);
        System.out.println(ans.size());
    }
    static void dfs(int start) {
        if (tmp.size() > 0) {
            ans.add(new LinkedList<>(tmp));
            System.out.println(tmp);
        }
        if (tmp.size() > 7) {
            return;
        }
        for (int i = start; i < chars.length; i++) {
            tmp.add(chars[i]);
            dfs(i + 1);
            tmp.removeLast();
        }
    }
}

答案:80

五、排序(贪心)

小蓝最近学习了一些排序算法,其中冒泡排序让他印象深刻。
在冒泡排序中,每次只能交换相邻的两个元素。

小蓝发现,如果对一个字符串中的字符排序,只允许交换相邻的两个字符,则在所有可能的排序方案中,冒泡排序的总交换次数是最少的。

例如,对于字符串 lan 排序,只需要 1 次交换。对于字符串 qiao 排序,总共需要 4 次交换。

小蓝找到了很多字符串试图排序,他恰巧碰到一个字符串,需要 100 次交换,可是他忘了吧这个字符串记下来,现在找不到了。

请帮助小蓝找一个只包含小写英文字母且没有字母重复出现的字符串,对该串的字符排序,正好需要 100 次交换。如果可能找到多个,请告诉小蓝最短的那个。如果最短的仍然有多个,请告诉小蓝字典序最小的那个。请注意字符串中不可以包含相同的字符。

Arrays.sort用多了,就怕这种专门考排序算法的题…

先把冒泡排序写下。

public class Main {
    public static void main(String[] args) {
        int[] nums = new int[]{9,8,2,3,4,2,1,5};
        int cnt = 0;
        // 进行len - 1次比较
        for (int i = 0; i < nums.length - 1; i++) {
            // 前面几个数会慢慢成有序
            for (int j = 0; j < nums.length - 1 - i; j++) {
                int tmp;
                if (nums[j] > nums[j + 1]) {
                    // 记录交换次数
                    cnt++;
                    tmp = nums[j];
                    nums[j] = nums[j + 1];
                    nums[j + 1] = tmp;
                }
            }
        }
        System.out.println(cnt);
        System.out.println(Arrays.toString(nums));
    }
}

字母都是小写,不会重复,那不就直接全排列,再拿去check下,看交换次数是不是100。(时间复杂度不允许)

再回到字符串本身,15个字符完全逆序onmlkjihgfedcba,需要105次交换,那么我们可以尝试把后面小的字符放到o前面,来看满足100次交换的最小情况,最后发现把j放到前面是最小字典序且能满足100次交换的情况。
答案:jonmlkihgfedcba

六、成绩分析(模拟)

在这里插入图片描述
在这里插入图片描述

import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        int n = scan.nextInt();
        int[] nums = new int[n];
        double sum = 0;
        int max = Integer.MIN_VALUE;
        int min = Integer.MAX_VALUE;
        for (int i = 0; i < n; i++) {
            nums[i] = scan.nextInt();
            sum += nums[i];
            if (nums[i] > max) {
                max = nums[i];
            }
            if (nums[i] < min) {
                min = nums[i];
            }
        }
        System.out.println(max);
        System.out.println(min);
        // 自动四舍五入
        System.out.printf("%.2f", sum / n);
    }
}

七、单词分析(模拟)

在这里插入图片描述
在这里插入图片描述

import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        String str = scan.nextLine();
        int[] cnt = new int[26];
        for (int i = 0; i < str.length(); i++) {
            // 只有小写字母
            cnt[str.charAt(i) - 'a']++;
        }
        int max = Integer.MIN_VALUE;
        char ans = 'a';
        for (int i = 0; i < 26; i++) {
            if (cnt[i] > max) {
                max = cnt[i];
                ans = (char)(97 + i);
            }
        }
        System.out.println(ans);
        System.out.println(max);
    }
}

八、数字三角形(DP)

在这里插入图片描述
先看看这道题,普通的数字三角形
在这里插入图片描述
这道题对左右移动次数没有限制,那就直接开搞。需要注意,数据可能为负数,所以在初始化时要为MIN_VALUE。

import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        int n = scan.nextInt();
        int[][] nums = new int[501][501];
        for (int i = 0; i < n; i++) {
            for (int j = 0; j <= i; j++) {
                nums[i][j] = scan.nextInt();
            }
        }
        int[][] dp = new int[501][501];
        // 注意有负数,所以注意要初始化为最小值
        for (int i = 0; i <= n; i++) {
            Arrays.fill(dp[i], Integer.MIN_VALUE);
        }
        dp[1][1] = nums[0][0];
        // 为了避免边界判断,可以直接把边界设置为1,1
        for (int i = 2; i <= n; i++) {
            for (int j = 1; j <= i; j++) {
                dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - 1]) + nums[i - 1][j - 1];
            }
        }
        // 从最后一排找最大值
        int ans = dp[n][1];
        for (int i = 2; i <= n; i++) {
            if (dp[n][i] > ans) {
                ans = dp[n][i];
            }
        }
        System.out.println(ans);
    }
}

上面的代码是向下爬,下面的代码是向上爬

import java.util.Scanner;

public class Main {
    public static void main (String[] args) {
        int[][] a = new int[510][510];
        int[][] f = new int[510][510];
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        for (int i = 1; i <= n; i ++) {
            for (int j = 1; j <= i; j++) {
                a[i][j] = sc.nextInt();
            }
        }

        for (int i = n; i >= 1; i--) {
        //从最后一排开始走,从下往上。
            for (int j = 1; j <= i; j++) {
                f[i][j] = Math.max(f[i + 1][j + 1], f[i + 1][j]) + a[i][j];
            }
        }

        System.out.println(f[1][1]);
    }
}

回到蓝桥杯的此题,就是多了左右移动次数的限制,那就把左右移动次数记录一下,然后最后一排再比较。

import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        int n = scan.nextInt();
        int[][] nums = new int[101][101];
        // 从三角形的顶部到底部有很多条不同的路径
        // 每一步只能从一个数走到下一层和它最近的左边的那个数或者右边的那个数
        // 每一步只能从一个数走到下一层和它最近的左边的那个数或者右边的那个数
        for (int i = 0; i < n; i++) {
            for (int j = 0; j <= i; j++) {
                nums[i][j] = scan.nextInt();
            }
        }
        int[][] dp = new int[101][101];
        int[][] move = new int[101][101];
        dp[1][1] = nums[0][0];
        for (int i = 2; i <= n; i++) {
            for (int j = 1; j <= i; j++) {
                if (dp[i - 1][j] > dp[i - 1][j - 1]) {
                    dp[i][j] = dp[i - 1][j] + nums[i - 1][j - 1];
                    // 左走 + 1
                    move[i][j] = move[i - 1][j] + 1;
                } else {
                    dp[i][j] = dp[i - 1][j - 1] + nums[i - 1][j - 1];
                    // 右走 - 1
                    move[i][j] = move[i - 1][j - 1] - 1;
                }
            }
        }
        int ans = Integer.MIN_VALUE;
        for (int i = 2; i <= n; i++) {
            // 大于ans且相差小于等于1
            if (dp[n][i] > ans && Math.abs(move[n][i]) <= 1) {
                ans = dp[n][i];
            }
        }
        System.out.println(ans);
    }
}

刚开始还想着,用三维DP数组,还是太蠢了。

九、子串分值和(找规律)

在这里插入图片描述
在这里插入图片描述

import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        String str = scan.nextLine();
        char[] chars = str.toCharArray();
        int[] dp = new int[100001];
        long sum = 0;
        for (int i = 0; i < str.length(); i++) {
            dp[i] = 1;
            HashSet<Character> map = new HashSet<>();
            map.add(chars[i]);
            for (int j = i + 1; j < str.length(); j++) {
                if (map.contains(chars[j])) {
                    dp[j] = dp[j - 1] + map.size();
                } else {
                    dp[j] = dp[j - 1] + map.size() + 1;
                    map.add(chars[j]);
                }
            }
            sum += dp[str.length() - 1];
        }
        System.out.println(sum);
    }
}

上面只能过60分,剩下的实例会超时,还要优化。优化方向应该朝着子串变化的规律性出发。
在这里插入图片描述
5 + 8 + 6 + 4 + 5 = 28
在这里插入图片描述
就是很巧妙啦~

转自:https://blog.csdn.net/weixin_46239370/article/details/115262482

十、装饰珠

待更待更…

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

@u@

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值