第12届蓝桥杯JavaB组省赛
第12届蓝桥杯JavaB组省赛
其他链接
第10届蓝桥杯JavaB组省赛 - Cattle_Horse
第11届蓝桥杯JavaB组省赛 - Cattle_Horse
第13届蓝桥杯javaB组省赛 - Cattle_Horse
前言
用时及分数
自测开始时间:\(2023年1月11日 16:00\)
自测结束时间:\(2023年1月11日 18:05\)
自测用时:约 \(2\) 小时
最终得分:\(20(35)\)
PS:此处得分依据蓝桥杯官网 \(OJ\) 测得
题目情况
完成题目:\(ABC(F)\)
\(F\) 题提交时的类名未使用 \(Main\),不得分。
\(D\) 题暴力写法跑了 \(30\) 分钟没跑出来
因为干饭提前结束测试
\(E\) 最短路没学或者说忘了
感受
\(12\) 届的题目填空题居然有 \(5\) 道,\(13\) 届才 \(2\) 道
而且感觉 \(13\) 届比 \(12\) 届更偏向算法知识了
注意点
-
提交时一定要注意类名等情况,或者建文件时就建多个 \(Main\)
-
心态放平,时间很充裕
-
实在写不出来正解,也尽量把暴力写出来
-
一定要把题目都看完
-
填空题提交之前一定要先对小数据进行测试
试题 A ASC
问题描述
已知大写字母 \(A\) 的 \(ASCII\) 码为 \(65\),请问大写字母 \(L\) 的 \(ASCII\) 码是多少?
答案为:\(76\)
Code
public class Main {
public static void main(String[] args) {
System.out.println((int) ('L'));//76
}
}
试题 B 卡片
问题描述
小蓝有很多数字卡片,每张卡片上都是数字 \(0\) 到 \(9\)。
小蓝准备用这些卡片来拼一些数,他想从 \(1\) 开始拼出正整数,每拼一个,就保存起来,卡片就不能用来拼其它数了。
小蓝想知道自己能从 \(1\) 拼到多少。
例如,当小蓝有 \(30\) 张卡片,其中 \(0\) 到 \(9\) 各 \(3\) 张,则小蓝可以拼出 \(1\) 到 \(10\),但是拼 \(11\) 时卡片 \(1\) 已经只有一张了,不够拼出 11。
现在小蓝手里有 \(0\) 到 \(9\) 的卡片各 \(2021\) 张,共 \(20210\) 张,请问小蓝可以从 \(1\) 拼到多少?
提示:建议使用计算机编程解决问题。
答案为:\(3181\)
思路
一个数组记录当前对应类型的牌剩余多少张,每算一个数字就减去所需的牌数
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
int[] arr = new int[10];
Arrays.fill(arr, 2021);
int now = 1;
while (true) {
int t = now;
while (t != 0) {
if (arr[t % 10] > 0) --arr[t % 10];
else {
//这里要输出now-1,因为当前这个牌是拼不出来的!!!
System.out.println(now-1);//3181
return;
}
t /= 10;
}
++now;
}
}
}
试题 C 直线
问题描述
在平面直角坐标系中,两点可以确定一条直线。如果有多点在一条直线上,那么这些点中任意两点确定的直线是同一条。
给定平面上 \(2 × 3\) 个整点 \(\{(x, y)|0 ≤ x < 2, 0 ≤ y < 3, x ∈ Z, y ∈ Z\}\),即横坐标是 \(0\) 到 \(1\) (包含 \(0\) 和 \(1\)) 之间的整数、纵坐标是 \(0\) 到 \(2\) (包含 \(0\) 和 \(2\)) 之间的整数的点。这些点一共确定了 \(11\) 条不同的直线。
给定平面上 \(20 × 21\) 个整点 \(\{(x, y)|0 ≤ x < 20, 0 ≤ y < 21, x ∈ Z, y ∈ Z\}\),即横坐标是 \(0\) 到 \(19\) (包含 \(0\) 和 \(19\)) 之间的整数、纵坐标是 \(0\) 到 \(20\) (包含 \(0\) 和 \(20\)) 之间的整数的点。请问这些点一共确定了多少条不同的直线。
答案为:\(40257\)
思路
既然是填空题,比赛的时候能暴力解就暴力解
既能很快的得到答案,也能减少出错的概率
具体思路见注释
import java.util.LinkedList;
import java.util.List;
public class Main {
static class Point {
public int x, y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
}
static class Line {
public Point A, B;
public Line(Point A, Point B) {
this.A = A;
this.B = B;
}
}
static List<Line> list = new LinkedList<>();
//判断点是否在该直线上
public static boolean checkPoint(Point p, Line l) {
return (p.y - l.A.y) * (p.x - l.B.x) == (p.y - l.B.y) * (p.x - l.A.x);
}
//判断两条直线是否为同一条
public static boolean checkLine(Line a, Line b) {
//先判断斜率是否相等
if ((a.B.y - a.A.y) * (b.B.x - b.A.x) != (b.B.y - b.A.y) * (a.B.x - a.A.x)) return false;
//如果斜率相等再取直线上一点,是否在另一直线上
return checkPoint(a.A, b);
}
//判断列表中是否存在该直线
public static boolean check(Line a) {
for (Line l : list) {
if (checkLine(a, l)) {
return true;
}
}
return false;
}
public static void main(String[] args) {
final int X = 20, Y = 21;
for (int x1 = 0; x1 < X; ++x1) {
for (int y1 = 0; y1 < Y; ++y1) {
Point a = new Point(x1, y1);
for (int x2 = 0; x2 < X; ++x2) {
for (int y2 = 0; y2 < Y; ++y2) {
//如果两点重合则不选
if (x2 == x1 && y2 == y1) continue;
// System.out.printf("a=(%d,%d),b=(%d,%d)\n", x1, y1, x2, y2);
Point b = new Point(x2, y2);
Line l = new Line(a, b);
if (!check(l)) list.add(l);
}
}
}
}
System.out.println(list.size());//40257
}
}
试题 D 货物摆放
问题描述
小蓝有一个超大的仓库,可以摆放很多货物。
现在,小蓝有 \(n\) 箱货物要摆放在仓库,每箱货物都是规则的正方体。小蓝规定了长、宽、高三个互相垂直的方向,每箱货物的边都必须严格平行于长、宽、高。
小蓝希望所有的货物最终摆成一个大的立方体。即在长、宽、高的方向上分别堆 \(L、W、H\) 的货物,满足 \(n = L \times W \times 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\) 位数字)时,总共有多少种方案?
提示:建议使用计算机编程解决问题。
答案为:\(2430\)
思路一
遍历第一个数 \(L\),如果是 \(n\) 的因子,说明当前数可取,然后遍历第二个数 \(W\)
由于 \(n = L × W × H\),当知道前两个数时,第 \(3\) 个数也随之确定
当 \(W\ne H\) 时,\(W\) 的值与 \(H\) 的值交换算是两种方案
因此,只需确定 \(W< \sqrt{\dfrac{n}{L}}\) 的 \(W\) 取值数量,然后乘以 \(2\)
再单独确定 \(W=\sqrt{\dfrac{n}{L}}\) 时是否满足条件,此时只算做一种方案
程序运行时间很长,最后没跑出来
public class Main {
public static void main(String[] args) {
final long n = 2021041820210418L;
long ans = 0;
for (long l = 1; l <= n; ++l) {
if (n % l != 0) continue;
final long rest = n / l;
long w;
for (w = 1; w * w < rest; ++w) {
if (rest % w == 0) ans += 2;
}
if (w * w == rest) ++ans;
}
System.out.println(ans);
}
}
思路二
比赛时最开始想到的思路
先处理出 \(n\) 的所有因子,再三重循环(或 \(DFS\))选出三个因子,相乘是否等于 \(n\)
public class Main {
public static void main(String[] args) {
long[] num = new long[1000];
int cnt = 0;
final long n = 2021041820210418L;
long now = 1;
while (now * now < n) {
if (n % now == 0) {
num[cnt++] = now;
num[cnt++] = n / now;
}
++now;
}
if (now * now == n) nums[cnt++] = now;
long ans = 0;
for (int i = 0; i < cnt; ++i) {
for (int j = 0; j < cnt; ++j) {
for (int k = 0; k < cnt; ++k) {
if (num[i] * num[j] * num[k] == n) {
++ans;
}
}
}
}
System.out.println(ans);//2430
}
}
判断优化
只需遍历两个因子,如果这两个因子的积仍为 \(n\) 的因子,则此时是一种方案
public class Main {
public static void main(String[] args) {
long[] num = new long[1000];
int cnt = 0;
final long n = 2021041820210418L;
long now = 1;
while (now * now < n) {
if (n % now == 0) {
num[cnt++] = now;
num[cnt++] = n / now;
}
++now;
}
if (now * now == n) nums[cnt++] = now;
long ans = 0;
for (int i = 0; i < cnt; ++i) {
for (int j = 0; j < cnt; ++j) {
if (n % (num[i] * num[j]) == 0) ++ans;
}
}
System.out.println(ans);//2430
}
}
思路三
若 \(L\leq W\leq H\),则:
- 当 \(L=W=H\) 时,生成方案数为 \(1\)
- 当 \(L=W < H\) 或 \(L< W=H\) 时,生成方案数为 \(3\)
- 当 \(L<W<H\) 时,生成方案数为 \(6\)
public class Main {
public static void main(String[] args) {
final long n = 2021041820210418L;
long ans = 0;
for (long i = 1; i * i * i <= n; ++i) {
if (n % i != 0) continue;
long rest = n / i;
for (long j = i; j * j <= rest; ++j) {
if (rest % j != 0) continue;
long k = rest / j;
if (i < j && j < k) ans += 6;
else if (i == j && j == k) ans += 1;
else ans += 3;
}
}
System.out.println(ans);//2430
}
}
试题 E 路径
问题描述
小蓝学习了最短路径之后特别高兴,他定义了一个特别的图,希望找到图中的最短路径。
小蓝的图由 \(2021\) 个结点组成,依次编号 \(1\) 至 \(2021\)。
对于两个不同的结点 \(a, b\),如果 \(a\) 和 \(b\) 的差的绝对值大于 \(21\),则两个结点之间没有边相连;
如果 \(a\) 和 \(b\) 的差的绝对值小于等于 \(21\),则两个点之间有一条长度为 \(a\) 和 \(b\) 的最小公倍数的无向边相连。
例如:结点 \(1\) 和结点 \(23\) 之间没有边相连;结点 \(3\) 和结点 \(24\) 之间有一条无向边,长度为 \(24\);结点 \(15\) 和结点 \(25\) 之间有一条无向边,长度为 \(75\)。
请计算,结点 \(1\) 和结点 \(2021\) 之间的最短路径长度是多少。
提示:建议使用计算机编程解决问题。
答案为:\(10266837\)
思路
前置知识:
\(Floyd\) 求全源最短路
import java.util.Arrays;
public class Main {
static int gcd(int x, int y) {
return y == 0 ? x : gcd(y, x % y);
}
static int lcm(int x, int y) {
return x / gcd(x, y) * y;
}
static final int N = 2021;
static final int INF = (int) 1e9;
//dis[i][j]表示i->j的路径长度
static int[][] dis = new int[N + 1][N + 1];
//运行Floyd之后,dis[i][j]变为 i->j的最短路径长度
static void Floyd() {
for (int k = 1; k <= N; ++k)
for (int i = 1; i <= N; ++i)
for (int j = 1; j <= N; ++j)
dis[i][j] = Math.min(dis[i][j], dis[i][k] + dis[k][j]);
}
public static void main(String[] args) {
for (int i = 0; i <= N; ++i) Arrays.fill(dis[i], INF);
for (int i = 1; i <= N; ++i) {
for (int j = i + 1; j <= N; ++j) {
if (j - i <= 21) {
dis[i][j] = dis[j][i] = lcm(i, j);
}
}
}
Floyd();
System.out.println(dis[1][2021]);//10266837
}
}
\(Dijkstra\) 求单源最短路
import java.util.Arrays;
import java.util.LinkedList;
import java.util.PriorityQueue;
public class Main {
static class edge {
int v, w;
public edge(int v, int w) {
this.v = v;
this.w = w;
}
}
static final int N = 2021;
static final int INF = 0x3f3f3f3f;
static LinkedList<edge>[] adj;
static int[] dis = new int[N + 1];
static boolean[] vis = new boolean[N + 1];
static void Dijkstra(int start) {
class node {
int v, dis;
public node(int v, int dis) {
this.v = v;
this.dis = dis;
}
}
//小根堆
PriorityQueue<node> q = new PriorityQueue<node>((o1, o2) -> o1.dis - o2.dis);
Arrays.fill(dis, INF);
dis[start] = 0;
q.add(new node(start, 0));
while (!q.isEmpty()) {
int u = q.poll().v;
if (vis[u]) continue;
vis[u] = true;
for (edge e : adj[u]) {
if (dis[e.v] > dis[u] + e.w) {
dis[e.v] = dis[u] + e.w;
q.add(new node(e.v, dis[e.v]));
}
}
}
}
public static void main(String[] args) {
adj = new LinkedList[N + 1];
for (int i = 1; i <= N; ++i) adj[i] = new LinkedList<edge>();
for (int i = 1; i <= N; ++i) {
for (int j = i + 1; j <= N; ++j) {
if (j - i <= 21) {
int LCM = lcm(i, j);
adj[i].add(new edge(j, LCM));
adj[j].add(new edge(i, LCM));
}
}
}
Dijkstra(1);
System.out.println(dis[N]);//10266837
}
static int gcd(int x, int y) {
return y == 0 ? x : gcd(y, x % y);
}
static int lcm(int x, int y) {
return x / gcd(x, y) * y;
}
}
试题 F 时间显示
问题描述
小蓝要和朋友合作开发一个时间显示的网站。在服务器上,朋友已经获取了当前的时间,用一个整数表示,值为从 \(1970\) 年 \(1\) 月 \(1\) 日 \(00:00:00\) 到当前时刻经过的毫秒数。
现在,小蓝要在客户端显示出这个时间。小蓝不用显示出年月日,只需要显示出时分秒即可,毫秒也不用显示,直接舍去即可。
给定一个用整数表示的时间,请将这个时间对应的时分秒输出。
【输入格式】
输入一行包含一个整数,表示时间。
【输出格式】
输出时分秒表示的当前时间,格式形如 \(HH:MM:SS\),其中 \(HH\) 表示时,值为 \(0\) 到 \(23\),\(MM\) 表示分,值为 \(0\) 到 \(59\),\(SS\) 表示秒,值为 \(0\) 到 \(59\)。
时、分、秒不足两位时补前导 0。
【样例输入 1】
46800999
【样例输出 1】
13:00:00
【样例输入 2】
1618708103123
【样例输出 2】
01:08:23
【评测用例规模与约定】
对于所有评测用例,给定的时间为不超过 \(10^{18}\) 的正整数
Code
import java.util.Scanner;
public class F {
//计算一天有多少毫秒
static final long dayMS = 24 * 60 * 60 * 1000;
public static void main(String[] args) {
long n = new Scanner(System.in).nextLong() % dayMS;
long h = n / (60 * 60 * 1000);
long m = n / (60 * 1000) % 60;
long s = n / 1000 % 60;
System.out.println(String.format("%02d:%02d:%02d", h, m, s));
}
}
试题 G 最少砝码
问题描述
你有一架天平。现在你要设计一套砝码,使得利用这些砝码可以称出任意小于等于 \(N\) 的正整数重量。
那么这套砝码最少需要包含多少个砝码?
注意砝码可以放在天平两边。
思路
推导
顺着推导由多少个砝码组合出 \(1\sim N\) 这些正整数不是很好想。
试着逆着推导,\(x\) 个砝码能组合出的最大的 \(1\sim n\)。
考虑多加 \(1\) 个砝码对前一种情况的影响。
假设有 \(1\) 个砝码,它的重量是 \(x\),新加入一个砝码,它的重量是 \(y\),那么能表达的数字分别是 \(\{x,y,|y-x|,y+x\}\),要使能组合的从 \(1\) 开始的连续的数最多,那么 \(\{x,y,|y-x|,y+x\}\) 应该互不相同且连续。
因为 \(y\) 本身能表达一个数字,要使他表达的数最多,则对于新加入的 \(y\) 应该大于 \(x\),因此 \(\{x,y,y-x,y+x\}\)。
再加入一个新的砝码,它的重量是 \(z\),那么能表达的数字是 \(\{\{x,y,y-x,y+x\},z+\{x,y,y-x,y+x\},z-\{x,y,y-x,y+x\},z\}\)。
同上,\(z>y>x\),且要使能组合的从 \(1\) 开始的连续的数最多,则能表达的数字应该互不相同且连续。
示例
例如:
-
有 \(1\) 个砝码,组合出的最大的 \(1\sim n\) 为 \(1\sim 1\)
-
有 \(2\) 个砝码,设加入重量为 \(x\) 的砝码(\(x>1\)),组合出的数为 \(\{1,x,x-1,x+1\}\)
要使其互不相同且连续,则 \(min(x,x-1,x+1)=1+1=2\),即 \(x-1=2\),也即 \(x=3\)
因此组合出 \(1\sim max(x,x-1,x+1)=x+1\),即 \(1\sim 4\) -
有 \(3\) 个砝码,设加入的重量为 \(x\) 的砝码(\(x>4\)),组合出的数为 \(\{\{1,2,3,4\},x+\{1,2,3,4\},x-\{1,2,3,4\},x\}\)
要使其互不相同且连续,则 \(min(x+\{1,2,3,4\},x-\{1,2,3,4\},x)=4+1=5\),即 \(x-4=5\),也即 \(x=9\)
因此组合出 \(1\sim max(x+\{1,2,3,4\},x-\{1,2,3,4\},x)=x+4\),即 \(1\sim 13\) -
有 \(k\) 个砝码,设其最多能组合出 \(1\sim n_k\),则加入第 \(k+1\) 个砝码,设其重量为 \(x\),组合出的数为 \(\{\{1\sim n_k\},x+\{1\sim n_k\},x-\{1\sim n_k\},x\}\)
要使其互不相同且连续,则 \(min(x+\{1\sim n_k\},x-\{1\sim n_k\},x)=n_k+1\),即 \(x-n_k=n_k+1\) ,也即 \(x=2\times n_k+1\)
因此组合出 \(1\sim max(x+\{1\sim n_k\},x-\{1\sim n_k\},x)=x+n_k\),即 \(1\sim 3\times n_k+1\)
总结
- 顺着题意推导可能没有思路的时候,可以考虑逆着推导
- 利用 前面有的结果
- 动态规划中常用前缀和等一些 \(1\sim n\) 前连续个数的某种性质
Code
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
int N = new Scanner(System.in).nextInt();
int k = 1, n = 1;//k为砝码个数,n为能表示的最大的连续数
while (n < N) {
++k;
n = 3 * n + 1;
}
System.out.println(k);
}
}
试题 H 杨辉三角形
问题描述
下面的图形是著名的杨辉三角形:
如果我们按从上到下、从左到右的顺序把所有数排成一列,可以得到如下数列:
\(1, 1, 1, 1, 2, 1, 1, 3, 3, 1, 1, 4, 6, 4, 1,\cdots\)
给定一个正整数 \(N\),请你输出数列中第一次出现 \(N\) 是在第几个数?
【输入格式】
输入一个整数 \(N\)。
【输出格式】
输出一个整数代表答案。
【样例输入】
\(6\)
【样例输出】
\(13\)
【评测用例规模与约定】
对于 \(20\%\) 的评测用例,\(1 ≤ N ≤ 10\);
对于所有评测用例,\(1 ≤ N ≤ 1000000000\)。
时间限制: \(5.0s\) 内存限制: \(512.0MB\)
思路
常规的杨辉三角形计算及输出如下:
final int N = 30;
int[][] triangle = new int[N + 1][N + 1];
for (int i = 1; i <= N; ++i) {
triangle[i][1] = 1;
for (int j = 2; j < i; ++j) triangle[i][j] = triangle[i - 1][j - 1] + triangle[i - 1][j];
triangle[i][i] = 1;
}
for (int i = 1; i <= N; ++i) {
for (int j = 1; j <= i; ++j) {
System.out.print(triangle[i][j] + " ");
}
System.out.println();
}
通过对杨辉三角数据的观察发现:
对于正整数 \(N\),在第 \(N+1\) 行 \(2\) 列一定会出现,因此最大范围应为 \([N+1][N+1]\)
而 \(N_{max}=10^9\) 很明显会超出内存限制,考虑内存优化
对于式子 \(triangle[i][j] = triangle[i - 1][j - 1] + triangle[i - 1][j]\) 只用到了前一行的两个数据,而前面的数据都是不再使用了的,所以采用滚动数组。
如果内循环仍从小到大更新,则当计算 \(triangle[j]\) 时,\(triangle[j-1]\) 已经被更新了,因此需要将前一行的数据记录。
final int N = 30;
int[][] triangle = new int[2][N + 1];
triangle[0][1] = triangle[1][1] = 1;
for (int i = 1; i <= N; ++i) {
System.out.print(1 + " ");
for (int j = 2; j <= i; ++j) {
triangle[i % 2][j] = triangle[(i - 1) % 2][j - 1] + triangle[(i - 1) % 2][j];
System.out.print(triangle[i % 2][j] + " ");
}
System.out.println();
}
更推荐逆序遍历,当计算 \(triangle[j]\)时,\(triangle[j-1]\) 仍为前一行的数据(与 \(01\) 背包相同)
final int N = 30;
int[] triangle = new int[N + 1];
triangle[1] = 1;
for (int i = 1; i <= N; ++i) {
for (int j = i; j >= 2; --j) triangle[j] += triangle[j - 1];
for (int j = 1; j <= i; ++j) System.out.print(triangle[j] + " ");
System.out.println();
}
杨辉三角,是二项式系数在三角形中的一种几何排列。
第 \(n\) 行 \(m\) 列元素通项公式为: \(C(n-1,m-1)=\dfrac{(n-1)!}{(m-1)!\times(n-m)!}\)
当 \(triangle[i][3]>N\) 时,对于 \(k>i\),\(j\) 为该行第 \(3\) 列到倒数第三列之间的列数,\(triangle[k][j]\) 一定也大于 \(N\)
如果在此之前的数均不为 \(N\),那么 \(N\) 一定在 \((N+1,2)\) 处取得,即 \(ans=\dfrac{(1+N)\times N}{2}+2\)
\(\begin{alignat}{1} C(n-1,3-1)&=C(n-1,2)\\ &=\dfrac{(n-1)!}{2\times(n-3)!}\\ &=\dfrac{(n-1)\times(n-2)}{2}\\ &<\dfrac{n^2}{2} \end{alignat} \)
则当 \(C(n-1,3-1)>N\) 时,\(n>\sqrt{2\times N}\),因此数组范围大于 \(\sqrt{2\times N}\) 即可
Code(90%)
import java.util.Scanner;
public class Main {
static long[] triangle;
public static void main(String[] args) {
final int N = new Scanner(System.in).nextInt();
if (N == 1) {
System.out.println(1);
return;
}
triangle = new long[(int) Math.sqrt(N)*2];
int ans = 1;
triangle[0] = 1;
for (int i = 1; i <= N; ++i) {
for (int j = i; j >= 1; --j) {
triangle[j] += triangle[j - 1];
if (triangle[j] == N) {
System.out.println(ans + i-j+ 1);
return;
}
}
if (triangle[2] > N) break;
ans += i + 1;
}
System.out.println(((1L + N) * N) / 2 + 2);
}
}
效率优化
由于杨辉三角是关于 \(y\) 轴对称的,因此可以只遍历一半的数据,要注意特殊处理偶数行的最大值
import java.util.Scanner;
public class Main {
static long[] triangle;
public static void main(String[] args) {
final int N = new Scanner(System.in).nextInt();
if (N == 1) {
System.out.println(1);
return;
}
triangle = new long[(int) Math.sqrt(N)*2];
int ans = 1;
triangle[0] = 1;
for (int i = 1; i <= N; ++i) {
if (i % 2 == 0) triangle[i / 2] = triangle[i / 2 - 1];
for (int j = i / 2; j >= 1; --j) {
triangle[j] += triangle[j - 1];
if (triangle[j] == N) {
System.out.println(ans + j + 1);
return;
}
}
if (triangle[2] > N) break;
ans += i + 1;
}
System.out.println(((1L + N) * N) / 2 + 2);
}
}
试题 I 双向排序
问题描述
给定序列 \((a_1 , a_2,\cdots, a_n) = (1, 2,\cdots, n)\),即 \(a_i = i\)。
小蓝将对这个序列进行 \(m\) 次操作,每次可能是将 \(a_1 , a_2 ,\cdots, a_{q_i}\) 降序排列, 或者将 \(a_{q_i} , a_{q_i+1} , · · · , a_n\) 升序排列。
请求出操作完成后的序列。
【输入格式】
输入的第一行包含两个整数 \(n, m\),分别表示序列的长度和操作次数。
接下来 \(m\) 行描述对序列的操作,其中第 \(i\) 行包含两个整数 \(p_i , q_i\) 表示操作
类型和参数。当 $p_i = 0 $ 时,表示将 \(a_1 , a_2 , \cdots , a_{q_i}\) 降序排列;当 \(p_i = 1\) 时,表示 将 \(a_{q_i} , a_{q_i+1} , · · · , a_n\) 升序排列。
【输出格式】
输出一行,包含 \(n\) 个整数,相邻的整数之间使用一个空格分隔,表示操作
完成后的序列。
【样例输入】
3 3
0 3
1 2
0 2
【样例输出】
3 1 2
思路(60%)
不会写,交暴力
时间复杂度 \(O(m\times n\times log(n))\)
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt(), m = sc.nextInt();
Integer[] a = new Integer[n + 1];
for (int i = 1; i <= n; ++i) a[i] = i;
for (int i = 0; i < m; ++i) {
int p = sc.nextInt(), q = sc.nextInt();
if (p == 1) Arrays.sort(a, q, n + 1);
else Arrays.sort(a, 1, q + 1, Collections.reverseOrder());
}
for (int i = 1; i <= n; ++i) System.out.print(a[i] + " ");
}
}