大家好,我是snippet,今天是刷蓝桥真题的第三天,下面是我今天的题解
目录
一、门牌制作
二、货物摆放
三、跳跃
四、重新排序
题目链接:门牌制作 - 蓝桥云课 (lanqiao.cn)
题目要求:
题目描述:
小蓝要为一条街的住户制作门牌号。
这条街一共有 2020 位住户,门牌号从 1 到 2020 编号。
小蓝制作门牌的方法是先制作 0 到 9 这几个数字字符,最后根据需要将字符粘贴到门牌上,例如门牌 1017 需要依次粘贴字符 1、0、1、7,即需要 1 个字符 0,2 个字符 1,1 个字符 7。
请问要制作所有的 1 到2020 号门牌,总共需要多少个字符 2?
运行限制:
最大运行时间:1s
最大运行内存: 128M
解题思路:
对1-2020每个数进行数位判断,如果为2则答案加一
代码:
package 蓝桥杯31天真题冲刺.Day3;
import java.io.*;
/**
* @author snippet
* @data 2023-03-05
* 门牌制作-蓝桥云课
*/
public class T1 {
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static PrintWriter pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)));
static int n = 2020;// n表示最大的用户
static int ans;// 表示使用2的个数
// 数位判断
static void check(int k) {
while (k != 0) {
int t = k % 10;
if (t == 2) ans++;
k /= 10;
}
}
public static void main(String[] args) throws IOException {
String[] s = br.readLine().split(" ");
n = Integer.parseInt(s[0]);
for (int i = 1; i <= n; i++) {
check(i);
}
pw.println(ans);
pw.flush();
br.close();
}
}
题目链接:货物摆放 - 蓝桥云课 (lanqiao.cn)
题目要求:
题目描述:
小蓝有一个超大的仓库,可以摆放很多货物。
现在,小蓝有 n 箱货物要摆放在仓库,每箱货物都是规则的正方体。小蓝规定了长、宽、高三个互相垂直的方向,每箱货物的边都必须严格平行于长、宽、高。
小蓝希望所有的货物最终摆成一个大的长方体。即在长、宽、高的方向上分别堆 L、W、H 的货物,满足 n=L×W×H。
给定 n,请问有多少种堆放货物的方案满足要求。
例如,当 n=4 时,有以下 6 种方案:1×1×4、1×2×2、1×4×1、2×1×2、2×2×1、4×1×1。
请问,当 n=2021041820210418 (注意有 16 位数字)时,总共有多少种方案?
提示:建议使用计算机编程解决问题。
答案提交:
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。
运行限制:
最大运行时间:1s
最大运行内存: 256M
解题思路:
直接3层for循环求满足 n = L*M*H 的方案数,如果直接从1-n遍历,则时间复杂度太大了(要运行几天才运行得出来),因为我们求的 L、M、N 三个数都是n的因数,所以我们可以先用一个集合把n的所有因数存起来,遍历L、M、N的时候就遍历n的所有因数,然后用一个变量进行计数就可以了
代码:
package 蓝桥杯31天真题冲刺.Day3;
import java.io.*;
import java.util.HashSet;
/**
* @author snippet
* @data 2023-03-05
* 货物摆放-蓝桥云课
*/
public class T2 {
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static PrintWriter pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)));
static long n = 2021041820210418l;
static int ans;
static HashSet set = new HashSet<>();
public static void main(String[] args) throws IOException {
// String[] s = br.readLine().split(" ");
// n = Long.parseLong(s[0]);
// 求出 n 的所有因数
for (long i = 1; i * i <= n; i++) {
if (n % i == 0) {
set.add(i);
set.add(n / i);
}
}
// 长、宽、高 分别对n的所有因数进行遍历(降低时间复杂度)
for (long i : set) {
for (long j : set) {
for (long k : set) {
if (i * j * k == n) ans++;
}
}
}
pw.println(ans);
pw.flush();
br.close();
}
}
题目链接:跳跃 - 蓝桥云课 (lanqiao.cn)
题目要求:
题目描述:
小蓝在一个 n 行 m 列的方格图中玩一个游戏。
开始时,小蓝站在方格图的左上角,即第 1 行第 1 列。
小蓝可以在方格图上走动,走动时,如果当前在第 r 行第 c 列,他不能走到行号比 r 小的行,也不能走到列号比 c 小的列。同时,他一步走的直线距离不超过 3。
例如,如果当前小蓝在第 3 行第 5 列,他下一步可以走到第 3 行第 6 列、第 3 行第 7 列、第 3 行第 8 列、第 4 行第 5 列、第 4 行第 6 列、第 4 行第 7 列、第 5 行第 5 列、第 5 行第 6 列、第 6 行第 5 列之一。
小蓝最终要走到第 n 行第 m 列。
在图中,有的位置有奖励,走上去即可获得,有的位置有惩罚,走上去就要接受惩罚。奖励和惩罚最终抽象成一个权值,奖励为正,惩罚为负。
小蓝希望,从第 1 行第 1 列走到第 n 行第 m 列后,总的权值和最大。请问最大是多少?
输入描述:
输入的第一行包含两个整数 n,m,表示图的大小。
接下来 n 行,每行 m 个整数,表示方格图中每个点的权值。
其中,1≤n≤100,−10^4≤权值≤10^4。
输出描述:
输出一个整数,表示最大权值和。
解题思路:
根据题意,我们求从起点到终点的最大权值路线就可以了,可以使用两种方法进行求解:
方法一:搜索(dfs),因为每个点的来源点都只有9种情况,我们先把这9种情况枚举出来,因为要求的是到终点的最大权值,那我们就可以从终点往前递归搜索,当找到的是起点的时候就回溯
方法二:动态规划(dp),根据题意我们可以对每个点进行状态转移,二维数组f存位置i->j的最大权值,也就是对每个点进行判断 看它从可能来源的9个位置的哪个位置到该点的权值最大,进行状态转移,状态转移式:f[i][j] = Math.max(f[i][j], f[x][y] + arr[i][j])
这个题的数据有点弱,我也不知道有没有写对,样例是过了,如果哪位佬发现有问题可以指出来,谢谢!
代码:
方法一:
package 蓝桥杯31天真题冲刺.Day3;
import java.io.*;
/**
* @author snippet
* @data 2023-03-05
* 跳跃-蓝桥云课
*/
public class T3_dfs {
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static PrintWriter pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)));
static int n,m;// n表示方格图的行数 m表示方格图的列数
static int[][] arr = new int[110][110];// 二维数组arr里面存整个方格图的所有数据
static int[][] f = new int[110][110];// 二维数组f里面存每个位置从(1,1)到该点的最大权值
// 只往下和右走 每次最多三步 所以该点的来源也只有9种情况
static int[] dx = {0, 0, 0, -1, -1, -1, -2, -2, -3};
static int[] dy = {-1, -2, -3, 0, -1, -2, 0, -1, 0};
// 搜索
// 从终点开始遍历 对它的所有来源点进行搜索
static void dfs(int x, int y) {
if (x == 1 && y == 1) {
f[1][1] = arr[1][1];
return;
}
for (int i = 0; i < 9; i++) {
int x1 = x + dx[i];
int y1 = y + dy[i];
if (x1 > 0 && y1 > 0) {
// 递归
dfs(x1, y1);
// 回溯
f[x][y] = Math.max(f[x][y], f[x1][y1] + arr[x][y]);
}
}
}
public static void main(String[] args) throws IOException {
String[] s = br.readLine().split(" ");
n = Integer.parseInt(s[0]);
m = Integer.parseInt(s[1]);
for (int i = 1; i <= n; i++) {
s = br.readLine().split(" ");
for (int j = 1; j <= m; j++) {
arr[i][j] = Integer.parseInt(s[j-1]);
}
}
dfs(n,m);
pw.println(f[n][m]);
pw.flush();
br.close();
}
}
方法二:
package 蓝桥杯31天真题冲刺.Day3;
import java.io.*;
import java.util.Arrays;
public class T3_dp {
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static PrintWriter pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)));
static int n, m;// n表示方格图的行数 m表示方格图的列数
static int[][] arr = new int[110][110];// 二维数组arr里面存整个方格图的所有数据
static int[][] f = new int[110][110];// 二维数组f里面存每个位置从(1,1)到该点的最大权值
// 只往下和右走 每次最多三步 所以该点的来源也只有9种情况
static int[] dx = {0, 0, 0, -1, -1, -1, -2, -2, -3};
static int[] dy = {-1, -2, -3, 0, -1, -2, 0, -1, 0};
public static void main(String[] args) throws IOException {
String[] s = br.readLine().split(" ");
n = Integer.parseInt(s[0]);
m = Integer.parseInt(s[1]);
for (int i = 1; i <= n; i++) {
s = br.readLine().split(" ");
for (int j = 1; j <= m; j++) {
arr[i][j] = Integer.parseInt(s[j - 1]);
}
}
for (int i = 0; i <= n; i++) Arrays.fill(f[i], -0x3f);
// 赋初始值
f[1][1] = arr[1][1];
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
// 对每个点的可能来源点进行判断 进行转移
for (int k = 0; k < 9; k++) {
int x = i + dx[k];
int y = j + dy[k];
if (x > 0 && y > 0) {
// 状态转移
f[i][j] = Math.max(f[i][j], f[x][y] + arr[i][j]);
}
}
}
}
pw.println(f[n][m]);
pw.flush();
br.close();
}
}
题目链接:重新排序 - 蓝桥云课 (lanqiao.cn)
题目要求:
问题描述:
给定一个数组 A 和一些查询 Li,Ri, 求数组中第Li 至第 Ri 个元素之和。
小蓝觉得这个问题很无聊, 于是他想重新排列一下数组, 使得最终每个查 询结果的和尽可能地大。小蓝想知道相比原数组, 所有查询结果的总和最多可以增加多少?
输入格式:
输入第一行包含一个整数 n 。
第二行包含 n 个整数 A1,A2,⋯,An, 相邻两个整数之间用一个空格分隔。
第三行包含一个整数 m 表示查询的数目。
接下来 m 行, 每行包含两个整数 Li、Ri, 相邻两个整数之间用一个空格分隔。
输出格式:
输出一行包含一个整数表示答案。
解题思路:
该题题意是 让我们求对数组重新排序后,我们多次区间查询后的和值与对原数组进行相同区间求和的最大差值
我们需要开辟一个数组a来存原始数据,在存入数据的时候,
我们可以再开辟一个数组b来对数据求前缀和,方便求原始的区间求和的值,
然后我们再开一个数组c记录区间求和的时候使用的数的出现次数(差分),
再对原始数组a和记录数据使用次数的数组c进行排序(从小到大),
因为是求最大的区间和 那我们让原始数组里面最大的数使用最多次就可以了(前提是数组c的数据 != 0 && 数组a里面还有数据可以遍历)(c[i] != 0 && index >= 1),
对数组c进行逆序遍历,再用一个下标index(从n开始遍历到1)来遍历数组a里面数据,sum_max += a[index] + c[i];
因为数据是到10^6,要记得 “十年oi一场空 不开long long见祖宗”
代码:
package 蓝桥杯31天真题冲刺.Day3;
import java.io.*;
import java.util.Arrays;
/**
* @author snippet
* @data 2023-03-06
* 重新排序-蓝桥云课
*/
public class T4 {
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static PrintWriter pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)));
static int n,m;// n表示数的个数 m表示查询的次数
static long sum,sum_max;// sum表示初始时的区间查询和 sum_max表示重新排序后的最大区间查询和
static int[] a = new int[1001000];// 一维数组a用来存初始数据
static long[] b = new long[1001000];// 一维数组b用来存i下标以及前面数据之和(前缀和)
static int[] c = new int[1001000];// 一维数组c用来存每个数被查询的次数
public static void main(String[] args) throws IOException {
String[] s = br.readLine().split(" ");
n = Integer.parseInt(s[0]);
s = br.readLine().split(" ");
for (int i = 1; i <= n; i++) {
a[i] = Integer.parseInt(s[i-1]);
b[i] = a[i];
}
// 求数据前缀和
for (int i = 1; i <= n; i++) {
b[i] += b[i-1];
}
s = br.readLine().split(" ");
m = Integer.parseInt(s[0]);
for (int i = 1; i <= m; i++) {
s = br.readLine().split(" ");
int l = Integer.parseInt(s[0]);
int r = Integer.parseInt(s[1]);
// 求原始区间查询和值
sum += b[r] - b[l-1];
// 先用差分记录每个数的出现次数
c[l]++;
c[r+1]--;
}
// 再用前缀和求出每个数出现的次数
for (int i = 1; i < c.length; i++) {
c[i] += c[i-1];
}
Arrays.sort(a, 1, n+1);
Arrays.sort(c);
int index = n;// 用index下标来记录使用的数字
for (int i = c.length-1; i > 0; i--) {
// 如果没有被使用过的数了 或者 能用的数都使用完了直接break;
if (c[i] == 0 || index < 1) break;
// 尽管贪 越大的数使用次数越多就可以了
sum_max += (long)a[index] * c[i];
index--;
}
pw.println(sum_max - sum);
pw.flush();
br.close();
}
}