大家好,我是snippet,今天是刷蓝桥真题的第六天,今天的题4有点难,下面是我今天的题解
目录
题目链接:最大子矩阵 - 蓝桥云课 (lanqiao.cn)
一、星期计算
题目链接:星期计算 - 蓝桥云课 (lanqiao.cn)
题目内容:
问题描述
本题为填空题,只需要算出结果后,在代码中使用输出语句将所填结果输出即可。
已知今天是星期六,请问 20^22 天后是星期几?
注意用数字 1 到 7 表示星期一到星期日。
运行限制
最大运行时间:1s
最大运行内存: 512M
解题思路:
因为题目给定了今天是星期六,那我们先把20^22%7求出,得到的x就是距离今天(星期六)的天数差距,因为输出1-7则表示星期一到星期日,那我们输出(x+6)%8即可
代码:
package 蓝桥杯31天真题冲刺.Day6;
import java.io.*;
import java.math.BigInteger;
/**
* @author snippet
* @data 2023-03-08
* 星期计算-蓝桥云课
*/
public class T1_星期计算 {
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static PrintWriter pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)));
static StreamTokenizer st = new StreamTokenizer(br);
public static void main(String[] args) throws IOException {
int ans = (int) (Math.pow(20,22)%7);
pw.println((ans+6)%8);
pw.flush();
br.close();
}
static int nextInt() throws IOException {
st.nextToken();
return (int)st.nval;
}
}
二、考勤刷卡
题目链接:考勤刷卡 - 蓝桥云课 (lanqiao.cn)
题目内容:
问题描述
小蓝负责一个公司的考勤系统, 他每天都需要根据员工刷卡的情况来确定 每个员工是否到岗。
当员工刷卡时, 会在后台留下一条记录, 包括刷卡的时间和员工编号, 只 要在一天中员工刷过一次卡, 就认为他到岗了。
现在小蓝导出了一天中所有员工的刷卡记录, 请将所有到岗员工的员工编 号列出。
输入格式
输入的第一行包含一个正整数 n, 表示一天中所有员工的刷卡记录的条数。 接下来 n 行, 每行包含一条刷卡记录, 每条刷卡记录的格式为:
HH:MM:SS ID
其中 HH:MM: SS 表示刷卡时间, HH 为一个 0 到 23 之间的两位十进制整数 (可能含前导 0 ) 表示时, MM 为一个 0 到 59 之间的两位十进制整数 (可能含前 导 0) 表示分, SS 为一个 0 到 59 之间的两位十进制整数 (可能含前导 0 ) 表 示秒, ID 为一个不含前导 0 的整数表示员工的编号。
所有记录按照刷卡时间升序排列, 可能同一时刻有多人刷卡。
输出格式
输出若干行, 每行包含一个整数, 按照从小到大的顺序输出, 表示到岗员 工的编号。
样例输入
4
13:05:42 103
14:07:12 4567
15:03:00 103
17:00:21 1
样例输出
1
103
4567
评测用例规模与约定
对于 50% 的评测用例,1≤n≤100。
对于所有评测用例,1≤n≤10000,员工编号为不超过 10^9 的正整数
运行限制
最大运行时间:1s
最大运行内存: 256M
解题思路:
因为我们要求的是员工的id编号,并且每个员工的id只可以出现一次,同时还要从小到大输出,那我们就可以直接使用TreeSet来存员工编号
代码:
package 蓝桥杯31天真题冲刺.Day6;
import java.io.*;
import java.util.TreeSet;
/**
* @author snippet
* @data 2023-03-08
* 考勤刷卡-蓝桥云课
*/
public class T2_考勤刷卡 {
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static PrintWriter pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)));
static StreamTokenizer st = new StreamTokenizer(br);
static int n;
static TreeSet<Integer> set = new TreeSet<>();
public static void main(String[] args) throws IOException {
String[] s = br.readLine().split(" ");
n = Integer.parseInt(s[0]);
for (int i = 0; i < n; i++) {
s = br.readLine().split(" ");
set.add(Integer.parseInt(s[1]));
}
for (int x : set) {
System.out.println(x);
}
pw.flush();
br.close();
}
static int nextInt() throws IOException {
st.nextToken();
return (int)st.nval;
}
}
三、卡片
题目链接:卡片 - 蓝桥云课 (lanqiao.cn)
题目内容:
问题描述
小蓝有 k 种卡片, 一个班有 n 位同学, 小蓝给每位同学发了两张卡片, 一 位同学的两张卡片可能是同一种, 也可能是不同种, 两张卡片没有顺序。没有两位同学的卡片都是一样的。
给定 n, 请问小蓝的卡片至少有多少种?
输入格式
输入一行包含一个正整数表示 n 。
输出格式
输出一行包含一个整数, 表示答案。
样例输入
6
样例输出
3
样例说明
小朋友们手中的卡片可能是: (1,1),(1,2),(1,3),(2,2),(2,3),(3,3)。
评测用例规模与约定
对于 50% 的评测用例, 1≤n≤10^4 。
对于所有评测用例, 1≤n≤10^9 。
运行限制
最大运行时间:1s
最大运行内存: 256M
解题思路:
根据题意,我们可以推出
一个学生最少需要1张卡片 例如:(1,1)
两个学生最少需要2张卡片 例如:(1,1)、(1,2)
三个学生最少需要2张卡片 例如:(1,1)、(1,2)、(2,2)
四个学生最少需要3张卡片 例如:(1,1)、(1,2)、(2,2)、(1,3)
五个学生最少需要3张卡片 例如:(1,1)、(1,2)、(2,2)、(1,3)、(2,3)
六个学生最少需要3张卡片 例如:(1,1)、(1,2)、(2,2)、(1,3)、(2,3)、(3,3)
七个学生最少需要4张卡片 例如:(1,1)、(1,2)、(2,2)、(1,3)、(2,3)、(3,3),(1,4)
那以此类推第8,9、10位同学最少都需要4张卡片,第11-15位同学最少需要5张卡片
根据上面的推断我们可以知道卡片增加是根据学生每次相对上一次多递增一个学生而增加一张卡片(初始值为1)
学生个数:1 3 6 10 15 21
卡片个数:1 2 3 4 5 6
代码:
package 蓝桥杯31天真题冲刺.Day6;
import java.io.*;
/**
* @author snippet
* @data 2023-03-08
* 卡片-蓝桥云课
*/
public class T3_卡片 {
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static PrintWriter pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)));
static StreamTokenizer st = new StreamTokenizer(br);
static int n;// n表示同学的个数
static int ans;// ans表示需要的卡片的最小数量
public static void main(String[] args) throws IOException {
n = nextInt();
int i = 1;
while (n > 0) {
n -= i;
ans++;
i++;
}
pw.println(ans);
pw.flush();
br.close();
}
static int nextInt() throws IOException {
st.nextToken();
return (int)st.nval;
}
}
四、最大子矩阵
题目链接:最大子矩阵 - 蓝桥云课 (lanqiao.cn)
题目内容:
问题描述
小明有一个大小为 N×M 的矩阵, 可以理解为一个 N 行 M 列的二维数组。
我们定义一个矩阵 m 的稳定度 f(m) 为 f(m)=max(m)−min(m), 其中 max(m) 表示矩阵 m 中的最大值,min(m) 表示矩阵 m 中的最小值。
现在小明想要从这个矩阵中找到一个稳定度不大于 limit 的子矩阵, 同时他还希望这个子矩阵的面积越大越好 (面积可以理解为矩阵中元素个数)。
子矩阵定义如下: 从原矩阵中选择一组连续的行和一组连续的列, 这些行列交点上的元素组成的矩阵即为一个子矩阵。
输入格式
第一行输入两个整数 N,M, 表示矩阵的大小。
接下来 N 行, 侮行输入 M 个整数,表示这个矩阵。
最后一行输入一个整数 limit, 表示限制。
辎出格式
输出一个整数. 分别表示小明选择的子矩阵的最大面积。
样例输入
3 4 2 0 7 9 0 6 9 7 8 4 6 4 8
样例输出
6
样例说明
满足稳定度不大于 8 的且面积最大的子矩阵总共有三个, 他们的面积都是 6 (粗体表示子矩阵元素)
2 0 7 9
0 6 9 7
8 4 6 4
2 0 7 9
0 6 9 7
8 4 6 4
2 0 7 9
0 6 9 7
8 4 6 4
评测用例规模与约定
对于所有评测用例, 0≤0≤ 矩阵元素值, limit ≤105≤105 。
运行限制
最大运行时间:5s
最大运行内存: 512M
解题思路:
方法一:暴力枚举+剪枝,控制判断的边界,当不满足的时候就进行剪枝
方法二:二分+单调队列,可以参考梗佬的题解:第十三届蓝桥杯JavaB组省赛F题——最大子矩阵 (AC)_蓝桥杯最大子矩阵_执 梗的博客-CSDN博客
代码:
方法一:
package 蓝桥杯30天真题冲刺.Day6;
import java.io.*;
/**
* @author snippet
* @data 2023-03-09
* 最大子矩阵-蓝桥云课
*/
// 暴力+剪枝可AC 控制边界即可
public class T4_最大子矩阵 {
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static PrintWriter pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)));
static StreamTokenizer st = new StreamTokenizer(br);
static int n,m,limit;
static long ans = Integer.MIN_VALUE;
static int N = 100010;
static int[][] arr;
static boolean check(int x1, int y1, int x2, int y2) {
int max = Integer.MIN_VALUE, min = Integer.MAX_VALUE;
for (int i = x1; i <= x2; i++) {
for (int j = y1; j <= y2; j++) {
max = Math.max(max, arr[i][j]);
min = Math.min(min, arr[i][j]);
}
}
return (max-min) <= limit;
}
public static void main(String[] args) throws IOException {
String[] s = br.readLine().split(" ");
n = Integer.parseInt(s[0]);
m = Integer.parseInt(s[1]);
arr = new int[n+1][m+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]);
}
}
s = br.readLine().split(" ");
limit = Integer.parseInt(s[0]);
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
for (int k = i; k <= n; k++) {
int r = Integer.MAX_VALUE;
int c = Integer.MAX_VALUE;
for (int l = j; l <= m; l++) {
// 控制边界
if (k >= r && l >= c) continue;
if (check(i,j,k,l)) ans = Math.max(ans, (k-i+1)*(l-j+1));
else {
r = k;
c = l;
break;
}
}
}
}
}
pw.println(ans);
pw.flush();
br.close();
}
static int nextInt() throws IOException {
st.nextToken();
return (int)st.nval;
}
}
方法二:
package 蓝桥杯30天真题冲刺.Day6;
import java.io.*;
import java.util.ArrayDeque;
import java.util.Deque;
/**
* @author snippet
* @data 2023-03-09
* 最大子矩阵-蓝桥云课
*/
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,limit,ans;
static int[][][] max;// 第k行 第i-j列的最大值
static int[][][] min;// 第k行 第i-j列的最小值
// 窗口大小为k
static boolean check(int x1, int x2, int k) {
Deque<Integer> qmax = new ArrayDeque<>();
Deque<Integer> qmin = new ArrayDeque<>();
for (int i = 1; i <= m; i++) {
// 处理最小值
// 控制窗口大小
if (!qmin.isEmpty() && qmin.peekFirst() < i - k + 1) qmin.pollFirst();
// 判断队列里面的数据是否满足要求
while (!qmin.isEmpty() && min[qmin.peekLast()][x1][x2] > min[i][x1][x2]) qmin.pollLast();
qmin.offerLast(i);
// 处理最大值
if (!qmax.isEmpty() && qmax.peekFirst() < i - k + 1) qmax.pollFirst();
while (!qmax.isEmpty() && max[qmax.peekLast()][x1][x2] < max[i][x1][x2])qmax.pollLast();
qmax.offerLast(i);
if (i >= k && max[qmax.peekFirst()][x1][x2] - min[qmin.peekFirst()][x1][x2] <= limit) return true;
}
return false;
}
public static void main(String[] args) throws IOException {
String[] s = br.readLine().split(" ");
n = Integer.parseInt(s[0]);
m = Integer.parseInt(s[1]);
max = new int[m+1][n+1][n+1];
min = new int[m+1][n+1][n+1];
for (int i = 1; i <= n; i++) {
s = br.readLine().split(" ");
for (int j = 1; j <= m; j++) {
max[j][i][i] = Integer.parseInt(s[j-1]);
min[j][i][i] = Integer.parseInt(s[j-1]);
}
}
s = br.readLine().split(" ");
limit = Integer.parseInt(s[0]);
// 数据预处理
for (int k = 1; k <= m; k++) {
for (int i = 1; i <= n; i++) {
// 注意j的赋值区间
for (int j = i + 1; j <= n; j++) {
max[k][i][j] = Math.max(max[k][i][j-1], max[k][j][j]);
min[k][i][j] = Math.min(min[k][i][j-1], min[k][j][j]);
}
}
}
// 二分
// 先枚举行(高度),二分找列(宽度)
for (int x1 = 1; x1 <= n; x1++) {
for (int x2 = x1; x2 <= n; x2++) {
int left = 1;
int right = m;
while (left < right) {
int mid = left + right + 1 >> 1;
if (check(x1, x2, mid)) left = mid;
else right = mid-1;
}
if (check(x1,x2,right)) ans = Math.max(ans, (x2-x1+1)*right);
}
}
pw.println(ans);
pw.flush();
br.close();
}
}