2023年第十四届蓝桥杯JavaB组省赛真题及全部解析(下)

news2024/12/26 22:14:44

承接上文:2023年第十四届蓝桥杯JavaB组省赛真题及全部解析(下)。

目录

七、试题 G:买二赠一

八、试题 H:合并石子 

九、试题 I:最大开支

十、试题 J:魔法阵


题目来自:蓝桥杯官网

七、试题 G:买二赠一

• 题目分析:

因为每次我们要尽可能的使免费拿走商品的价格尽可能的大,但是免费的金额又与较便宜的物品有关,这是一道贪心题(猜的,比赛想到就可以写了,贪心的证明是非常恶心的)。

• 解题思路:

1. 排序商品价格(降序)。

2. 使用队列来存储免费的金额。

3. 遍历全部商品,遇到能免费的商品(小于队列队头元素),出队列,这个商品跳过(不加到总和)。其他的正常遍历,加到总和。

4. 一旦选了两个商品,将后面选的(肯定小于前面选的)一半价格加入队列。

5. 返回结果。

如果下面的排序写法看不懂的话,可以去看我之前写的Java sort 详解,里面都有写到。

• 代码编写:

import java.util.*;
public class Main{
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        //读入数据
        Integer[] arr = new Integer[n];
        for(int i = 0;i < n;i++){
            arr[i] = in.nextInt();
        }
        Arrays.sort(arr,(o1,o2) -> {//从大到小排序
            return o1 >= o2 ? -1 : 1;//或者(o2 - o1)
        });
        long sum = 0;//存储总花费
        Queue<Integer> q = new LinkedList<>();//存储可以免费的金额,保证是先进先出
        int count = 0;//记录何时到达二次
        for(int i = 0;i < n;i++){
            if(!q.isEmpty() && q.peek() >= arr[i]) {//说明可以免费带走
                q.poll();//队头免费金额用过了,把队头弹出去。
                continue;//跳过这个商品
            }
            sum += arr[i];
            count++;
            if(count == 2){
                count = 0;
                q.add(arr[i] / 2);//可以免费的金额,存入队列
            }
        }
        System.out.println(sum);
    }
}

• 运行结果: 

八、试题 H:合并石子 

• 题目分析:

这是一道区间 dp 的问题,关于区间 dp 可能平时会遇到的比较少。如果不了解的话,建议先去看看区间 dp,再去洛谷把 合并石子 (这道题的简化版)做了。

• 解题思路:

1. 状态表示:

dp[i][j][k]:表示合并区间[i , j]为 1 堆,且颜色为 k 的最小花费。

其他的参数说明:

sum[i]:表示前 i 堆石头的和(前缀和方便后续求值)。

cost[ij][j]:表示从在区间[ i ,j ]的最小花费(dp表示的是 1 堆,最后结果不一定为 1 堆,所以要重新创建一个)。

nums[i][j]:表示在区间[ i ,j ]的最小堆数。

2. 状态转移方程:

dp[i][j][(k + 1) % 3] 可以由分割点 j 的左边花费数 + 分割点 j 的右边花费数 + 合并两堆的花费数,转移过来。

dp[i][end][(k + 1) % 3] = Math.min(dp[i][end][(k + 1) % 3],dp[i][j][k] + dp[j + 1][end][k] + sum[end] - sum[i - 1]);

3.初始化:

把dp[ i ][ i ][col[ i ] ]初始为0,因为这本来就是 1 堆,不用合并花费。

其他的代码里面都有注释就不多赘述了。

• 代码编写:

import java.util.*;
public class Main {
    static int INF = 0x3f3f3f3f;
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int[] col = new int[n + 1];//存储颜色
        int[] sum = new int[n + 1];//前缀和
        int[][] cost = new int[n + 1][n + 1];//表示i 到 j 区间的花费
        int[][] nums = new int[n + 1][n + 1];//区间的堆数
        //读入数据
        for(int i = 1;i <= n;i++){
            sum[i] = sum[i - 1] + in.nextInt();
        }
        for(int i = 1;i <= n;i++){
            col[i] = in.nextInt();
        }
        //初始化
        int[][][] dp = new int[n + 1][n + 1][3];
        for(int i = 1;i <= n;i++){
            for(int j = 1;j <= n;j++){
                nums[i][j] = j - i + 1;//独自成一堆
                Arrays.fill(dp[i][j],INF);//求最小值,防止被 0 干预
            }
            dp[i][i][col[i]] = 0;//只有自己且颜色存在
        }
        //填写 dp 表
        for(int len = 1;len <= n;len++){//枚举长度
            for(int i = 1;i + len - 1 <= n;i++){//枚举起点
                int end = i + len - 1;//找到终点
                for(int k = 0;k < 3;k++){//枚举颜色
                    for(int j = i;j < end;j++){//枚举分割点
                        if(dp[i][j][k] != INF && dp[j + 1][end][k] != INF){//去掉不存在的节点
                            dp[i][end][(k + 1) % 3] = Math.min(dp[i][end][(k + 1) % 3],dp[i][j][k] + dp[j + 1][end][k] + sum[end] - sum[i - 1]);
                            nums[i][end] = 1;//注意我们的状态就是表示合成一堆
                        }
                    }
                }
            }
        }
        //将堆数为 1 的花费填入 cost(只能填 1 )
        for(int i = 1;i <= n;i++){
            for(int j = i;j <= n;j++){
                if(nums[i][j] == 1){
                    cost[i][j] = Math.min(dp[i][j][0],Math.min(dp[i][j][1],dp[i][j][2]));
                }
            }
        }
        //dp 表示是合成 1 堆,但是最后不一定能合成一堆,所以要再查找一次。
        //把所有区间都枚举出来,找到最小堆数的最小花费
        for(int k = 1;k <= n;k++){
            for(int i = 1;i <= k;i++){
                for(int j = k + 1;j <= n;j++){
                    if(nums[i][j] > nums[i][k] + nums[k + 1][j]){
                        nums[i][j] = nums[i][k] + nums[k + 1][j];
                        cost[i][j] = cost[i][k] + cost[k + 1][j];
                    }else if(nums[i][j] == nums[i][k] + nums[k + 1][j]){
                        cost[i][j] = Math.min(cost[i][j],cost[i][k] + cost[k + 1][j]);
                    }
                }
            }
        }
        System.out.println(nums[1][n] + " " + cost[1][n]);
    }
}

• 运行结果: 

九、试题 I:最大开支

 • 题目分析:

这题不能使用动态规划来做,因为时间复杂度至少为O(n^2),题目给出的数据为 10 ^ 5,会超时的,所以我们要另找方法。

考虑到H函数在乘于人数后,会变成一个一元二次方程 k * x ^ 2 + bx(k为负数);,开口向下,先递增后递减,并且递增的速度越来越慢,也就是说,随着项目参与人数的增加,总花费的增加(后一个减去前一个)会变得越来越少。因此我们贪心的点就来了,在往项目中增加人数时,我们每次都选取花费增加最多的项目添加。由局部最优,带来全局最优。

• 解题思路:

先算出一个花费增加数的公式:

我们配合优先级队列就能达到O(n * log(n))的时间复杂度。

• 代码编写:

import java.util.*;


public class Main{
    static long sum = 0;//最初最终的总和
    static int[] k;
    static int[] b;
    static int[] cnt;//记录对应项目的人数
    static class Pair{//不能使用Java自带的,会遍历错误,很奇怪
        int key;//表示可以直接加到总和的花费
        int val;//表示第 val 个项目
        public Pair(int key,int val){
            this.key = key;
            this.val = val;
        }
    }

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt(),m = in.nextInt();
        k = new int[m + 1];
        b = new int[m + 1];
        cnt = new int[m + 1];
        //大根堆
        PriorityQueue<Pair> q = new PriorityQueue<>((o1,o2) -> {
            return o1.key > o2.key ? -1 : 1;
        });
        //读入数据
        for(int i = 1;i <= m;i++){
            k[i] = in.nextInt();
            b[i] = in.nextInt();
            cnt[i] = 1;
            q.add(new Pair(mul(cnt[i],i),i));
        }
        for(int i = 0;i < n;i++){
            Pair tmp = q.poll();
            if(tmp.key <= 0){//小于0说明后续都小于0,直接退出即可
                break;
            }
            sum += tmp.key;
            Integer t = tmp.val;
            q.add(new Pair((k[t] * (2 * cnt[t] + 1) + b[t]),t));//推导出来的公式。
            cnt[t]++;//对应的人数 + 1
        }
        System.out.println(sum);
    }
    public static int mul(int x,int i){
        return (k[i] * x + b[i]) * x;//题目给出的公式
    }
}

 • 运行结果: 


十、试题 J:魔法阵

• 解题思路:

利用动态规划 + Dijkstra 算法的一些思想来做。

1. 状态表示:

dp[i][j]:表示从节点 0 到节点 i ,使用魔法消除最后 j 条边的最小总伤害。

2. 状态转移方程:

下面都是以 i 为终点,u 为起点来推的,w 为 u -> i 的权值。

• 不使用魔法:dp[i][0] = min(dp[i][0] , dp[u][0] + w);

• 使用魔法:dp[i][j] = min(dp[u][j - 1],dp[i][j]);其中 1<= j <= k。

• 魔法使用过了:dp[i][k] = min(dp[i][k] , dp[u][k] + w);

3. 初始化:

dp表除了 [0][0] 初始化为 0 ,其它的初始化为无穷大。

4. 填表:

配合 Dijkstra 算法进行填表。

5. 返回值:

返回 dp[n - 1][k]即可。

剩下的一些零碎在代码中都有注释。

• 代码编写:

import java.util.*;
public class Main {
    static class Pair<K,V>{//不能使用 Java 自带的,会编译不过去
        K v;
        V w;
        public Pair(K v,V w){
            this.v = v;
            this.w = w;
        }
    }
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt(),k = in.nextInt(),m = in.nextInt();
        int INF = 0x3f3f3f3f;
        List<List<Pair<Integer,Integer>>> ret = new ArrayList<>();//使用邻接表的方式存储边
        for(int i = 0;i < n;i++){
            ret.add(new ArrayList<>());
        }
        //1.创建 dp 表
        int[][] dp = new int[n][k + 1];
        for(int i = 0;i < n;i++){
            Arrays.fill(dp[i],INF);
        }
        //2.初始化
        dp[0][0] = 0;
        for(int i = 0;i < m;i++){
            int u = in.nextInt(),v = in.nextInt(),w = in.nextInt();
            ret.get(u).add(new Pair<>(v,w));
            ret.get(v).add(new Pair<>(u,w));//无向图,所以两边都要存储边
        }
        //3.填表
        Queue<Integer> q = new LinkedList<>();//用来存放起点
        q.add(0);
        while(!q.isEmpty()){
            int u = q.poll();
            for(Pair<Integer,Integer> pair:ret.get(u)){//取出边
//                下面是 Dijkstra 算法的思路(不是完全一样)
                int v = pair.v;
                int w = pair.w;
                boolean flag = false;//表示还有没有从 v 节点为起点的必要,
                // 如果为 false 的话说明以 v 为起点绝对不是最小路径,要舍去,同时还可以防止死循环
                if(dp[v][0] > dp[u][0] + w){//选取最优
                    flag = true;
                    dp[v][0] = dp[u][0] + w;
                }

                for(int j = 1;j <= k;j++){
                    if(dp[v][j] > dp[u][j - 1]){
                        flag = true;
                        dp[v][j] = dp[u][j - 1];
                    }
                }
                if(dp[v][k] > dp[u][k] + w){
                    flag = true;
                    dp[v][k] = dp[u][k] + w;
                }
                if(flag == true){
                    q.add(v);
                }
            }
        }
        //4.返回值
//        System.out.println(Math.min(dp[n - 1][0],dp[n - 1][k]));
        System.out.println(dp[n - 1][k]);//都行
    }
}

 • 运行结果:  

结语:

其实写博客不仅仅是为了教大家,同时这也有利于我巩固知识点,和做一个学习的总结,由于作者水平有限,对文章有任何问题还请指出,非常感谢。如果大家有所收获的话还请不要吝啬你们的点赞收藏和关注,这可以激励我写出更加优秀的文章。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1872697.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

嵌入式Linux系统编程 — 4.5 strcmp、strchr 等函数实现字符串比较与查找

目录 1 字符串比较 1.1 strcmp() 函数 1.2 strncmp() 函数 1.3 示例程序 2 字符串查找 2.1 strchr() 函数 2.2 strrchr() 函数 2.3 strstr() 函数 2.4 strpbrk() 函数 2.5 示例程序 1 字符串比较 strcmp() 和 strncmp() 函数是C语言标准库中用于比较两个字符串的函…

Node.js实验指南:完善接口服务器

上一章接口服务器&#xff0c;我们实现了一个异常简单的接口服务器。可能很多人会感觉有点不真实的感觉&#xff0c;接口这么简单吗&#xff1f;没错&#xff0c;就这么简单。 我们在真实项目的前后端对接中&#xff0c;调用接口&#xff0c;拿到数据&#xff0c;就是如此而已…

20240624(周一)AH股行情总结:A股低开低走, 恒生科技指数跌2%,贵州茅台转跌为涨

内容提要 A股三大指数收盘均跌超1%&#xff0c;半导体、智慧医疗、商业航天概念领跌&#xff0c;中芯国际跌超3%&#xff0c;盘中一度涨2%。水电股逆势走强&#xff1b;白酒股低开高走&#xff0c;贵州茅台翻红&#xff0c;盘初曾跌3%。微盘股指数大跌4%。 正文 周一&#x…

社区便民团购小程序源码系统 前后端分离 带完整源代码包以及搭建部署教程

系统概述 随着移动互联网的快速发展&#xff0c;社区团购凭借其便利性、优惠性逐渐走进人们的生活&#xff0c;成为了日常生活不可或缺的一部分。为了满足市场对此类服务的需求&#xff0c;我们特别推出了一款社区便民团购小程序源码系统&#xff0c;该系统采用前后端分离架构…

高考选专业分析,裁员潮下计算机专业还值得选择吗?

本文首发于公众号“AntDream”&#xff0c;欢迎微信搜索“AntDream”或扫描文章底部二维码关注&#xff0c;和我一起每天进步一点点 选择大学专业是一项非常重要的决策&#xff0c;尤其是在当前外部环境变化及技术快速发展的背景下。对于“是否还应该选择计算机专业”这个问题&…

C#/.NET量化开发实现财富自由【4】实现EMA、MACD技术指标的计算

听说大A又回到了2950点以下&#xff0c;对于量化交易来说&#xff0c;可能这些都不是事儿。例如&#xff0c;你可以预判到大A到顶了&#xff0c;你可能早就跑路了。判断逃顶还是抄底&#xff0c;最简单的方式就是判断是否顶背离还是底背离&#xff0c;例如通过MACD&#xff0c;…

C语言基础笔记(全)

一、数据类型 数据的输入输出 1.数据类型 常量变量 1.1 数据类型 1.2 常量 程序运行中值不发生变化的量&#xff0c;常量又可分为整型、实型(也称浮点型)、字符型和字符串型 1.3 变量 变量代表内存中具有特定属性的存储单元&#xff0c;用来存放数据&#xff0c;即变量的值&a…

Java基础知识-线程

Java基础知识-线程 1、在 Java 中要想实现多线程代码有几种手段&#xff1f; 1. 一种是继承 Thread 类 2. 另一种就是实现 Runnable 接口 3. 最后一种就是实现 Callable 接口 4. 第四种也是实现 callable 接口&#xff0c;只不过有返回值而已 2、Thread 类中的 start() 和 …

有序充电在新能源行业的前景与应用

作为新能源汽车的基础动力装置&#xff0c;交流充电桩也是可以促使新能源汽车正常行驶的关键内容。近年来我国新能源汽车的增长速度出现明显的上升趋势&#xff0c;但是其充电桩的发展还比较缓慢。目前在充电桩系统设计期间仍存在一些问题&#xff0c;主要表现在充电设施短缺、…

2024年6月京东睡眠呼吸暂停和低通气事件检测赛题-baseline

赛题地址&#xff1a;DC竞赛-大数据竞赛平台 (datacastle.cn) 一、数据集介绍 train_x训练数据集特征描述&#xff0c;其样本分布不均匀&#xff0c;0样本29808&#xff0c;1样本3221&#xff0c;2样本4520&#xff0c;共计37549条样本 第一维度&#xff1a;60 位受试样本数总…

SSL证书在网站访问中的核心作用及快速申请指南

在当今的互联网时代&#xff0c;数据安全与用户隐私保护成为了网站运营不可或缺的一部分。SSL证书作为一种重要的网络安全协议&#xff0c;它在网站访问中扮演着至关重要的角色&#xff0c;主要体现在以下几个方面&#xff1a; 一、加密通信内容&#xff1a;SSL证书通过建立安…

2024年【G1工业锅炉司炉】考试及G1工业锅炉司炉考试题库

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2024年G1工业锅炉司炉考试为正在备考G1工业锅炉司炉操作证的学员准备的理论考试专题&#xff0c;每个月更新的G1工业锅炉司炉考试题库祝您顺利通过G1工业锅炉司炉考试。 1、【多选题】TSGG0001-2012《锅炉安全技术监察…

MySQL实训

项目名称与项目简介 股票交易系统是一个综合性的金融服务平台&#xff0c;它提供了股票买卖、交易查询、用户管理、股票信息管理以及资金账户管理等功能。系统旨在为用户提供一个安全、高效、便捷的股票交易环境&#xff0c;让用户能够实时掌握市场动态&#xff0c;做出合理的…

golang项目基于gorm框架从postgre数据库迁移到达梦数据库的实践

一、安装达梦数据库 1、登录达梦数据库官网&#xff0c;下载对应系统版本的安装包。 2、下载地址为&#xff1a;https://www.dameng.com/list_103.html 3、达梦数据库对大小写敏感&#xff0c;在安装初始化数据库实例时建议忽略大小写&#xff1b;具体安装教程可参考以下博客: …

知不知行不行?

老树新芽&#xff0c; 结合最近几个观点&#xff0c;再拓展&#xff1a; Prompt & “Prompt—生成—选择” 事前提要求&#xff0c;事后决定满意不满意&#xff0c;中间尽量少干涉——老板对员工不也是这样吗&#xff1f;甲方对乙方不也是这样吗&#xff1f;都是后者在生…

VMware虚拟机移植保姆级教程

文章目录 前言:一、打包与备份二、VMware移植1. 文件介绍2. 移植过程总结:前言: 前几日对电脑做了一个大的更新升级,不仅将硬件进行了升级,还对电脑的软件进行了升级也就是我从Win10今家庭版升级到Win11专业版啦,之前没有升级是因为数据量很多,怕升级后找不到自己需要的…

vue-cli搭建过程

1.vue-cli 概述 vue-cli 官方提供的一个脚手架&#xff0c;用于快速生成一个 vue 的项目模板&#xff0c;预先定义好的目录结构及基础代码 举个例子吧&#xff01; 比如之前学过的Maven,在创建 Maven 项目时可以选择创建一个骨架项目&#xff0c;这个骨架项目就是脚手架&#x…

goLang小案例-打印99乘法表

goLang小案例-打印99乘法表 1. 打印99乘法表 func Print99multiplication1() {//横向9排for i : 1; i < 9; i {//竖向9列//第一批第一个 和第一列比较 如果大于排就结束//假设第三排i3 最走有三列 1*3 2*3 3*3//j3打印完 j 当j4就要结束 以此类推for j : 1; j < i; j …

东兴市金顺心贸易有限公司联合越南名厨研发的阿吉贡河粉灵魂汤底料

东兴市金顺心贸易有限公司联合越南名厨研发的阿吉贡河粉灵魂汤底料&#xff0c;一包汤底料竟然就能撑起一家店的灵魂&#xff01;&#x1f372; 简单的食材&#xff0c;却散发出不平凡的美味&#xff0c;仿佛带我穿越千里之外。每一口都是满满的幸福与满足&#xff0c;真心推荐…

Detailed Steps for Troubleshooting ORA-00600 [kdsgrp1] (文档 ID 1492150.1)

Detailed Steps for Troubleshooting ORA-00600 [kdsgrp1] (文档 ID 1492150.1)​编辑转到底部 In this Document Purpose Troubleshooting Steps References APPLIES TO: Oracle Database - Enterprise Edition Oracle Database Cloud Schema Service - Version N/A and lat…