【蓝桥杯冲刺 day10】题目全解析 --- 难题突破

Hello大家好,我是秋刀鱼,今天又来给大家更新蓝桥杯真题的题解了。

今天的题目偏难也是耗费了我大量的时间构思与码代码,如果到现在你还没有看过题建议先自己思考后在看看我的博客,一定要多思考才能有进步!!

如果觉得作者写的还不错的朋友可以一键三连支持一下,听说点赞的朋友今年都会暴富!

ღ( ´・ᴗ・` )比心

image-20220317174954880

相关的蓝桥杯算法题解:

【蓝桥杯冲刺 day8】题目全解析 —附上LeetCode 每日一题

【蓝桥杯冲刺 day7】 题目全解析 — 附上LeetCode周赛 银联-03. 理财产品

【蓝桥杯冲刺 day4】题目全解析 — 每日刷题解析

【蓝桥杯】算法训练 无聊的逗

【蓝桥杯】算法提高 打包

【蓝桥杯】算法提高 学生节

题目链接

⭐️为知识点重要程度

扫地机器人 ⭐️⭐️⭐️⭐️

全球变暖 ⭐️⭐️⭐️⭐️⭐️

机器人行走 ⭐️⭐️⭐️

数的幂次 ⭐️⭐️⭐️

扫地机器人

题目描述

小明公司的办公区有一条长长的走廊,由 N 个方格区域组成,如下图所示。

img

走廊内部署了 K 台扫地机器人,其中第 台在第 Ai 个方格区域中。已知扫地机器人每分钟可以移动到左右相邻的方格中,并将该区域清扫干净。

请你编写一个程序,计算每台机器人的清扫路线,使得

  1. 它们最终都返回出发方格,
  2. 每个方格区域都至少被清扫一遍,
  3. 从机器人开始行动到最后一台机器人归位花费的时间最少。

注意多台机器人可以同时清扫同一方块区域,它们不会互相影响。

输出最少花费的时间。 在上图所示的例子中,最少花费时间是 6。第一台路线:2-1-2-3-4-3-2,清 扫了 1、2、3、4 号区域。第二台路线 5-6-7-6-5,清扫了 5、6、7。第三台路线 10-9-8-9-10,清扫了 8、9 和 10。

输入格式:
第一行输入两个整数:N、K
第二行输入K个整数,表示扫地机器人的位置
输出格式:
输出一个整数,表示移动步数最多的机器人移动的步数
案例:
输入:
10 3
3 5 8
输出:
6
说明:
总移动步数最少的方案为:
1号机器人路线:3->2->1->2->3->4->3
2号机器人路线:5->6->7->6->5
3号机器人路线:8->9->10->9->8

解题思路

题目中要求给出最多的机器人移动步数,例如上例中最左侧机器人的移动距离步数是最多的:共移动了六格,不难发现除了其开始所在的方格之外,一共清扫了 3 个方格,进一步推导不难得出,移动的步数 = 除开始所在方格外连续清洗方格数的两倍。

为什么能够除去开始所在的方格呢?

移动到开始时方格所消耗的步数为0,所以可以不予考虑。

那么我将所求转换为:除其所在方格外擦洗最多连续(每次只能移动一格)方格的数目 * 2.

那么我们设机器人最多连续擦洗的方格数为 n u m num num ,取两个极端的情况,如果所有方格都被放置了一个机器人,也就是说 n u m num num 只有可能是 0 ,因为所有机器人不需要移动就能完成擦洗任务;如果一共 N N N 个方格,只有一台机器人,那么该机器人需要擦洗所有方格, n u m = N − 1 num = N - 1 num=N1,也就是说 n u m ∈ [ 0 , N − 1 ] num\in[0,N-1] num[0,N1]

知道了区间范围,可以使用二分的方法,令 m i d mid mid为中间值,假设答案 m i d mid mid能够满足清洗条件,此时答案区间就能缩减为: n u m ∈ [ 0 , m i d ] num\in[0,mid] num[0,mid],否则答案区间缩减为: n u m ∈ [ m i d + 1 , N − 1 ] num\in[mid+1,N-1] num[mid1,N1],继续遍历直到找到答案。

image-20220317163551263

拿上面的例子举例,此时枚举的答案值为: m i d mid midcheck函数核心的思想:每一台机器人优先清洗左侧方格,如果还有剩余次数:清洗右侧方格直到次数为0或右侧有其他机器人。

对于第一台机器人 R 1 R_1 R1 R 1 R_1 R1 左侧没有其他机器人,但是其左侧有方块没有被清理。如果这时右侧机器人 R 2 R_2 R2帮他清理左侧方格,这显然不符合我们希望找到最小的机器人移动最大步数这个前提,、左侧区域只能够由 R 1 R_1 R1 来清洗才能保证最小值,其余的任何机器人都无法帮助 R 1 R_1 R1清洗该区域。如果另左侧需要清理方格数量 为 r e d red red ,会有下面两种情况:

  • 如果 r e d > m i d red > mid red>mid,说明 m i d mid mid 答案下无法清理,因此直接返回false
  • 否则, R 1 R_1 R1 能够清左侧的方块,此时mid-red 表示能够清理的方块数目减少。但是如果剩余的最大清理次数,为了尽可能使用完 m i d mid mid 次,此时 R 1 R_1 R1可以向右侧清理 m i d − r e d mid-red midred 格,让 R 2 R_2 R2需要清理的左侧需要清理的方格尽可能的少。我们能够根据 R 1 R_1 R1 R 2 R_2 R2的间隔值与 R 1 R_1 R1帮助清理的方格数能够算出 遍历到 R 2 R_2 R2 时的 r e d red red 值。循环判断 R 2 R_2 R2 能够完成清理…

最终根据check的范围值不断缩小区间,最终获得答案值为: l ∗ 2 l *2 l2

AC代码

// 1:无需package
// 2: 类名必须Main, 不可修改

import java.io.*;
import java.util.Arrays;

public class Main {
    
    
    // m 为当前的最大擦洗次数
    public static boolean check(int n,int m,int []robots){
    
    
        // 左侧长度
        int red = robots[0];
        for (int i = 0; i < robots.length; ++i) {
    
    

            if (m < red) {
    
    
                return false;
            }
            int num = m - red;
            // 计算距离下一个机器人的长度
            red = (i == robots.length - 1 ? n : robots[i + 1]) - robots[i] - 1;
            // 能帮助右侧机器人清扫
            if (num > 0) {
    
    
                red = Math.max(0, red - num);
            }
        }
        return red == 0;
    }

    public static void main(String[] args) throws IOException {
    
    
        PrintWriter writer = new PrintWriter(new OutputStreamWriter(System.out));
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        StreamTokenizer in = new StreamTokenizer(reader);

        int n,k;
        int[] robots;
        in.nextToken();
        n = (int) in.nval;
        in.nextToken();
        k = (int) in.nval;
        robots = new int[k];
        for (int i = 0; i < k; ++i) {
    
    
            in.nextToken();
            // 更改为存储索引
            robots[i] = (int) in.nval - 1;
        }
        // 按照机器人的索引位置排序
        Arrays.sort(robots);
        int l, r;
        // 开始的区间范围 [0,n-1]
        l=0;
        r = (n - 1);
        while (l < r) {
    
    
            int m = l + (r - l) / 2;
            if (check(n, m, robots)) {
    
    
                r = m;
            }else{
    
    
                l = m + 1;
            }
        }
        System.out.println(l << 1);

        writer.flush();
    }
}

全球变暖

题目描述

你有一张某海域NxN像素的照片,".“表示海洋、”#"表示陆地,如下所示:

7
.......
.##....
.##....
....##.
..####.
...###.
.......

其中"上下左右"四个方向上连在一起的一片陆地组成一座岛屿。例如上图就有2座岛屿。

由于全球变暖导致了海面上升,科学家预测未来几十年,岛屿边缘一个像素的范围会被海水淹没。具体来说如果一块陆地像素与海洋相邻(上下左右四个相邻像素中有海洋),它就会被淹没。

例如上图中的海域未来会变成如下样子:





…#…

请你计算:依照科学家的预测,照片中有多少岛屿会被完全淹没。

【输入格式】
第一行包含一个整数N。 (1 <= N <= 1000)
以下N行N列代表一张海域照片。

照片保证第1行、第1列、第N行、第N列的像素都是海洋。

【输出格式】
一个整数表示答案。

【输入样例】

7
.......
.##....
.##....
....##.
..####.
...###.
.......

资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗 < 1000ms

写在前面

这道题跟我做过的LeetCode上的一道题很像,如果感兴趣的朋友可以练练手:岛屿的数量

开始时我的解题思路是,先求出岛屿的数量,在将挨着水的陆地涂为其他字符,再次求出岛屿的数量,最后相间得到消失的岛屿数量。一提交好家伙超时了。。。

解题思路

核心的思路是深度、广度优先遍历的方式,定义 p r e pre pre存储变暖前的岛屿数量,定义 a f t e r after after存储变暖后的岛屿数量。

使用 f o r for for 循环找到符号#,表示该位置是属于岛屿的位置,此时发现了一座岛执行: pre+=1,从这个位置开始遍历与该位置相连接的所有没有遍历到的岛屿方块。如果遍历到的岛屿方块邻接水域,那么将该岛屿方块值置为K,否则置为W(值任意取看自己的喜好)。如果该方块在判断后没有被邻接水域,说明该岛屿没有被完全淹没,可以返回一个大于0的常数,此时after+=1

最终pre-after就是结果值。

AC代码

import java.io.*;

public class Main {
    
    
    private static final int[][] DIRECTS = new int[][]{
    
    {
    
    1, 0}, {
    
    -1, 0}, {
    
    0, 1}, {
    
    0, -1}};

    public static int islands(char[][] picture, int x, int y) {
    
    
        int bk = 0;
        int n = picture.length;
        // 判断是否邻接水域
        for (int[] direct : DIRECTS) {
    
    
            int nx = x + direct[0];
            int ny = y + direct[1];
            if (nx < 0 || ny < 0 || nx >= n || ny >= n || picture[nx][ny] != '.') {
    
    
                continue;
            }
            picture[x][y]='K';
        }
        // 判断变暖后任然是岛屿
        if (picture[x][y] == '#') {
    
    
            picture[x][y] = 'W';
            ++bk;
        }
        for (int[] direct : DIRECTS) {
    
    
            int nx = x + direct[0];
            int ny = y + direct[1];
            if (nx < 0 || ny < 0 || nx >= n || ny >= n) {
    
    
                continue;
            }
            // 继续遍历没有遍历过的岛屿
            if (picture[nx][ny] == '#') {
    
    
                bk += islands(picture, nx, ny);
            }
        }
        return bk;
    }
    public static void main(String[] args) throws IOException {
    
    
        PrintWriter writer = new PrintWriter(new OutputStreamWriter(System.out));
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        StreamTokenizer in = new StreamTokenizer(reader);

        int n;
        char[][] pictures;
        in.nextToken();
        n = (int)in.nval;
        pictures = new char[n][n];
        for (int i = 0; i < n; ++i) {
    
    
            pictures[i] = reader.readLine().toCharArray();
        }
        int pre = 0, after = 0;
        for (int i = 0; i < n; ++i) {
    
    
            for (int j = 0; j < n; ++j) {
    
    
                if (pictures[i][j] == '#') {
    
    
                    ++pre;
                    if (islands(pictures, i, j) > 0) {
    
    
                        ++after;
                    }
                }
            }
        }
        writer.println((pre - after));
        writer.flush();
    }
}

机器人行走

题目描述

某少年宫引进了一批机器人小车。可以接受预先输入的指令,按指令行动。小车的基本动作很简单,只有3种:左转(记为L),右转(记为R),向前走若干厘米(直接记数字)。

例如,我们可以对小车输入如下的指令:

15L10R5LRR10R20

则,小车先直行15厘米,左转,再走10厘米,再右转,…

不难看出,对于此指令串,小车又回到了出发地。

你的任务是:编写程序,由用户输入指令,程序输出每条指令执行后小车位置与指令执行前小车位置的直线距离。

【输入、输出格式要求】

用户先输入一个整数n(n<100),表示接下来将有n条指令。

接下来输入n条指令。每条指令只由L、R和数字组成(数字是0~100之间的整数)

每条指令的长度不超过256个字符。

程序则输出n行结果。

每条结果表示小车执行相应的指令前后位置的直线距离。要求四舍五入到小数后2位。

例如:用户输入:

5
L100R50R10
3LLL5RR4L12
LL
100R
5L5L5L5

则程序输出:

102.96
9.06
0.00
100.00
0.00

解题思路

这题还算简单,使用变量存储移动到的位置、目前的朝向。使用 D I R E C T S DIRECTS DIRECTS 数组来存储方向移动的距离,注意顺序!存储数据的顺序必须是逆时针或顺时针转动的顺序。

AC代码

// 1:无需package
// 2: 类名必须Main, 不可修改

import java.io.*;
import java.util.LinkedList;
import java.util.Queue;

public class Main {
    
    
    public static void main(String[] args) throws IOException {
    
    
        PrintWriter writer = new PrintWriter(new OutputStreamWriter(System.out));
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        StreamTokenizer in = new StreamTokenizer(reader);

        final int[][] DIRECTS = new int[][]{
    
    {
    
    1, 0}, {
    
    0, -1}, {
    
    -1, 0}, {
    
    0, 1}};
        int n;
        String[] orders;
        in.nextToken();
        n = (int) in.nval;
        orders = new String[n];
        for (int i = 0; i < n; ++i) {
    
    
            orders[i] = reader.readLine();
        }

        for (String order : orders) {
    
    
            int len = order.length();
            int x, y;
            x = y = 0;
            int forward = 0;
            for (int i = 0; i < len; ++i) {
    
    
                if (order.charAt(i) == 'L') {
    
    
                    ++forward;
                }else if(order.charAt(i)=='R'){
    
    
                    --forward;
                }else{
    
    
                    int val = 0;
                    while (i != len && (order.charAt(i) != 'L' && order.charAt(i) != 'R')) {
    
    
                        val *= 10;
                        val += order.charAt(i) - '0';
                        ++i;
                    }
                    --i;
                    x += DIRECTS[forward][0] * val;
                    y += DIRECTS[forward][1] * val;
                }
                // 防止为负数与大于3
                forward = (forward + 4) % 4;
            }
            writer.printf("%.2f\n", Math.sqrt((x * x) + (y * y)));
        }

        writer.flush();
    }
}

数的次幂

题目描述

image-20220317161205962

解题思路

如果熟悉快速幂算法的同学应该这题是能够轻易拿下的,本题就是一道快速幂的模板题,只是增加了一个取余的步骤。

如果没有学习过快速幂小伙伴可以看看这篇博客,快速幂取模算法详解

快速幂核心思想是:将原有的多个’'小数’乘法运算差分为少量大数的乘法运算,举一个例子来简单说说快速幂:

4 10 = ( 16 ) 5 = 1 6 4 ∗ 16 = ( 256 ) 2 ∗ 16 = ( 65535 ) ∗ 16 4^{10} = (16)^5=16^4*16 = (256)^2*16=(65535)*16 410=(16)5=16416=(256)216=(65535)16
通过不断地差分幂次方10 来快速计算,将原来需要的10次乘法运算,简化为如上的大数运算节省了大量的时间。

AC代码

// 1:无需package
// 2: 类名必须Main, 不可修改

import java.io.*;

public class Main {
    
    
    static PrintWriter writer = new PrintWriter(new OutputStreamWriter(System.out));
    static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
    static StreamTokenizer in = new StreamTokenizer(reader);
    public static int nextInt() throws IOException {
    
    
        in.nextToken();
        return (int)in.nval;
    }

    private static long fastPower(long x, long y, long mod){
    
    
      // 返回的结果
        long base = 1;
        while (y > 0) {
    
    
            if ((y & 1) == 1) {
    
    
                base = base * x % mod;
            }
            x = x * x % mod;
            y >>= 1;
        }
        return base;
    }

    public static void main(String[] args) throws IOException {
    
    
        int n;
        n = nextInt();
        for (int i = 0; i < n; ++i) {
    
    
            System.out.println(fastPower(nextInt(), nextInt(), nextInt()));
        }
    }
}



写在最后

如果说代码、论述中有任何问题,欢迎大家指出,同时如果有任何疑问,也能够在评论区中留言,大家共同讨论共同进步!

img

猜你喜欢

转载自blog.csdn.net/qq_51439643/article/details/123556544