蓝桥杯 2019年国赛真题
Java 大学A组
试题 A: 三升序列
本题总分: 5 5 5 分
【问题描述】
对于一个字母矩阵,我们称矩阵中的一个三升序列是指在矩阵中找到三个字母,它们在同一行,同一列,或者在同一 45 45 45 度的斜线上,这三个字母从左向右看、或者从上向下看是递增的。
例如,如下矩阵中
YQPD
BKEZ
AFYV
有 B K Z \mathrm{BKZ} BKZ、 B E Z \mathrm{BEZ} BEZ、 A F Y \mathrm{AFY} AFY、 A F V \mathrm{AFV} AFV、 A K P \mathrm{AKP} AKP、 D E F \mathrm{DEF} DEF 等 6 6 6 个三升序列。注意当三个字母是从左下到右上排列时,从左向右看和从上向下看是不同的顺序。
对于下面的 30 30 30 行 50 50 50 列的矩阵,请问总共有多少个三升序列?(如果你把以下文字复制到文本文件中,请务必检查复制的内容是否与文档中的一致。在试题目录下有一个文件 inc.txt,内容与下面的文本相同)
VLPWJVVNNZSWFGHSFRBCOIJTPYNEURPIGKQGPSXUGNELGRVZAG
SDLLOVGRTWEYZKKXNKIRWGZWXWRHKXFASATDWZAPZRNHTNNGQF
ZGUGXVQDQAEAHOQEADMWWXFBXECKAVIGPTKTTQFWSWPKRPSMGA
BDGMGYHAOPPRRHKYZCMFZEDELCALTBSWNTAODXYVHQNDASUFRL
YVYWQZUTEPFSFXLTZBMBQETXGXFUEBHGMJKBPNIHMYOELYZIKH
ZYZHSLTCGNANNXTUJGBYKUOJMGOGRDPKEUGVHNZJZHDUNRERBU
XFPTZKTPVQPJEMBHNTUBSMIYEGXNWQSBZMHMDRZZMJPZQTCWLR
ZNXOKBITTPSHEXWHZXFLWEMPZTBVNKNYSHCIQRIKQHFRAYWOPG
MHJKFYYBQSDPOVJICWWGGCOZSBGLSOXOFDAADZYEOBKDDTMQPA
VIDPIGELBYMEVQLASLQRUKMXSEWGHRSFVXOMHSJWWXHIBCGVIF
GWRFRFLHAMYWYZOIQODBIHHRIIMWJWJGYPFAHZZWJKRGOISUJC
EKQKKPNEYCBWOQHTYFHHQZRLFNDOVXTWASSQWXKBIVTKTUIASK
PEKNJFIVBKOZUEPPHIWLUBFUDWPIDRJKAZVJKPBRHCRMGNMFWW
CGZAXHXPDELTACGUWBXWNNZNDQYYCIQRJCULIEBQBLLMJEUSZP
RWHHQMBIJWTQPUFNAESPZHAQARNIDUCRYQAZMNVRVZUJOZUDGS
PFGAYBDEECHUXFUZIKAXYDFWJNSAOPJYWUIEJSCORRBVQHCHMR
JNVIPVEMQSHCCAXMWEFSYIGFPIXNIDXOTXTNBCHSHUZGKXFECL
YZBAIIOTWLREPZISBGJLQDALKZUKEQMKLDIPXJEPENEIPWFDLP
HBQKWJFLSEXVILKYPNSWUZLDCRTAYUUPEITQJEITZRQMMAQNLN
DQDJGOWMBFKAIGWEAJOISPFPLULIWVVALLIIHBGEZLGRHRCKGF
LXYPCVPNUKSWCCGXEYTEBAWRLWDWNHHNNNWQNIIBUCGUJYMRYW
CZDKISKUSBPFHVGSAVJBDMNPSDKFRXVVPLVAQUGVUJEXSZFGFQ
IYIJGISUANRAXTGQLAVFMQTICKQAHLEBGHAVOVVPEXIMLFWIYI
ZIIFSOPCMAWCBPKWZBUQPQLGSNIBFADUUJJHPAIUVVNWNWKDZB
HGTEEIISFGIUEUOWXVTPJDVACYQYFQUCXOXOSSMXLZDQESHXKP
FEBZHJAGIFGXSMRDKGONGELOALLSYDVILRWAPXXBPOOSWZNEAS
VJGMAOFLGYIFLJTEKDNIWHJAABCASFMAKIENSYIZZSLRSUIPCJ
BMQGMPDRCPGWKTPLOTAINXZAAJWCPUJHPOUYWNWHZAKCDMZDSR
RRARTVHZYYCEDXJQNQAINQVDJCZCZLCQWQQIKUYMYMOVMNCBVY
ABTCRRUXVGYLZILFLOFYVWFFBZNFWDZOADRDCLIRFKBFBHMAXX
【答案提交】
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。
180414
这里程序做的是,枚举三升序列的中间元素,以统计出全部的三升序列,
为了增加代码的重用性,我们使用统计某段连续的元素构成的子序列,从左向右看递增的三升序列程序来统计从上向下看递增的三升序列。
矩阵中的元素可以朝 8 8 8 个方向连续,按行号的增量 x x x 与列号的增量 y y y 构成的二元组 ( x , y ) (x,y) (x,y),分别记为: ( 0 , 1 ) (0,1) (0,1)、 ( − 1 , 1 ) (-1,1) (−1,1)、 ( − 1 , 0 ) (-1,0) (−1,0)、 ( − 1 , − 1 ) (-1,-1) (−1,−1)、 ( 0 , − 1 ) (0,-1) (0,−1)、 ( 1 , − 1 ) (1,-1) (1,−1)、 ( 1 , 0 ) (1,0) (1,0)、 ( 1 , 1 ) (1,1) (1,1)。
其中 ( 0 , 1 ) (0,1) (0,1)、 ( − 1 , 1 ) (-1,1) (−1,1)、 ( 1 , 0 ) (1,0) (1,0)、 ( 1 , 1 ) (1,1) (1,1) 与其余 4 4 4 个方向相逆且均能使用统计 ( 0 , 1 ) (0,1) (0,1) 方向三升序列程序来统计,但我们还能发现 ( − 1 , 1 ) (-1,1) (−1,1) 从上往下看为三升序列时,从左向右恰反,故而再统计一遍其逆方向 ( 1 , − 1 ) (1,-1) (1,−1)。
import java.io.FileInputStream;
public class Test {
static int N = 30, M = 50, K = 5;
int[][] matrix = new int[N][M], offset = {
{
1, 0}, {
0, 1}, {
-1, 1}, {
1, -1}, {
1, 1}};
public static void main(String[] args) {
new Test().run(); }
void run() {
try(FileInputStream in = new FileInputStream("inc.txt")) {
int ans = 0, less, more, x, y;
for (int i = 0, bt; i < N; ++i)
for (int j = 0; j < M; ++j) {
do bt = in.read();
while (bt <= 0x20 || bt == 0x7f);
matrix[i][j] = bt;
}
for (int i = 0; i < N; ++i)
for (int j = 0; j < M; ++j)
for (int k = 0; k < K; ++k) {
less = more = 0;
for (x = i, y = j; x >= 0 && y >= 0 && x < N && y < M; x -= offset[k][0], y -= offset[k][1])
if (matrix[x][y] < matrix[i][j]) ++less;
for (x = i, y = j; x >= 0 && y >= 0 && x < N && y < M; x += offset[k][0], y += offset[k][1])
if (matrix[x][y] > matrix[i][j]) ++more;
ans += less * more;
}
System.out.println(ans);
} catch (Exception e) {
e.printStackTrace();
}
}
}
试题 B: 切割
本题总分: 5 5 5 分
【问题描述】
在 4 × 4 4\times4 4×4 的方格矩阵中画一条直线。则直线穿过的方格集合有多少种不同的可能?
这个里直线穿过一个方格当且仅当直线将该方格分割成面积都大于 0 0 0 的两部分。
【答案提交】
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。
267
据说是 267 267 267,但我编的程序计算不出来,先挂着吧。
public class Test {
public static void main(String[] args) {
new Test().run(); }
double accuracy = 0.01;
int N = 4, M = 4, ans = 0;
int[][] index = new int[N][M];
boolean[][] zero = new boolean[N + 1][M + 1];
boolean[][] positive = new boolean[N + 1][M + 1];
boolean[] map = new boolean[1 << N * M];
void run() {
for (int i = 0; i < N; ++i)
for (int j = 0; j < M; ++j)
index[i][j] = 1 << i * M + j;
for (double x = 0; x <= N; x += accuracy) {
for (double y = 0; y <= M; y += accuracy) {
total(y / -x, y); // (x1:x, y1:0) (x2:0, y2:y)
total(y / (N - x), -x * y / (N - x)); // (x1:x, y1:0) (x2:N, y2:y)
}
for (double x2 = 0; x2 <= N; x2 += accuracy)
total(M / (x2 - x), -x * M / (x2 - x)); // (x1:x, y1:0) (x2:x2, y2:M)
}
for (double y = 0; y <= M; y += accuracy) {
for (double x = 0; x <= N; x += accuracy)
total((M - y) / x, y); // (x1:0, y1:y) (x2:x, y2:M)
for (double y2 = 0; y2 <= M; y2 += accuracy)
total((y2 - y) / N, y); // (x1:0, y1:y) (x2:N, y2:y2)
}
for (boolean mark : map) if (mark) ++ans;
System.out.println(ans);
}
void total(double k, double b) {
for (int x = 0; x <= N; ++x)
for (int y = 0; y <= M; ++y) {
double tmp = k * x + b - y;
zero[x][y] = abs(tmp) <= 1e-10;
positive[x][y] = tmp > 1e-10;
}
int tot = 0;
for (int x = 0; x < N; ++x)
for (int y = 0; y < M; ++y) {
if (positive[x][y] == positive[x + 1][y] &&
positive[x][y] == positive[x][y + 1] &&
positive[x][y] == positive[x + 1][y + 1]) continue;
if (zero[x][y] && zero[x + 1][y] && zero[x][y + 1] && zero[x + 1][y + 1]) continue;
tot |= index[x][y];
}
map[tot] = true;
}
double abs(double a) {
return a > 0 ? a : -a; }
}
试题 C: 最优旅行
本题总分: 10 10 10 分
【问题描述】
中国的高铁四通八达,乘坐方便,小明经常乘坐高铁在城市间旅游。
现在,小明又有了一个长假,他打算继续乘坐高铁旅游。这次,他打算到下面的城市旅游。
上海、广州、长沙、西安、杭州、济南、成都、南京、昆明、郑州、天津、太原、武汉、重庆、南昌、长春、沈阳、贵阳、福州。
小明打算从北京出发,游览以上每个城市正好一次,最终回到北京。在每个城市(除北京外),小明都至少停留 24 24 24 小时。而当小明决定从一个城市去往另一个城市时,他只会选择有直接高铁连接的城市,不会在中途换乘转车。
在试题目录下有一个文件 t r i p . t x t \mathrm{trip.txt} trip.txt 保存了小明可以选择的车次,小明不会选择其他车次。
小明出发的时间是第 1 1 1 天的中午 12 12 12: 00 00 00。请问,小明游览完以上城市正好一次,最终回到北京,最快需要多少分钟(请注意单位为分钟,请注意除北京外的城市需要至少停留 24 24 24 小时,即最少停留 1440 1440 1440 分钟)。
【答案提交】
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。
41613
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.time.LocalTime;
import java.util.*;
public class Test {
public static void main(String[] args) {
new Test().run(); }
Map<String, List<Train>> graph = new HashMap();
Map<String, Boolean> visited = new HashMap();
int depth = 0, ans = 0x3f3f3f3f;
void run() {
try(BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream("trip.txt"), "GBK"))) {
String line = in.readLine();
while ((line = in.readLine()) != null) {
StringTokenizer token = new StringTokenizer(line);
token.nextToken();
String u = token.nextToken();
String v = token.nextToken();
int start = LocalTime.parse(token.nextToken()).toSecondOfDay() / 60;
int end = LocalTime.parse(token.nextToken()).toSecondOfDay() / 60;
if (!graph.containsKey(u)) {
graph.put(u, new ArrayList());
visited.put(u, false);
}
graph.get(u).add(new Train(v, start, end));
}
dfs("北京", (visited.size() - 1) * 1440 + 720);
System.out.println(ans - 720);
} catch (Exception e) {
e.fillInStackTrace();
}
}
void dfs(String start, int time) {
if (time > ans) return;
if (depth == visited.size() && start.equals("北京")) ans = min(ans, time);
for (Train train : graph.get(start)) {
if (visited.get(train.to)) continue;
visited.put(train.to, true);
++depth;
if (time % 1440 > train.start)
dfs(train.to, time + 1440 - time % 1440 + train.end);
else
dfs(train.to, time - time % 1440 + train.end);
visited.put(train.to, false);
--depth;
}
}
int min(int arg1, int arg2) {
return arg1 < arg2 ? arg1 : arg2; }
class Train {
String to;
int start, end;
Train(String to, int start, int end) {
this.to = to;
this.start = start;
this.end = end;
}
}
}
很莫名其妙的一道搜索题,这里没做字符串到整型的映射是因为,你编写完这部分程序,你写的暴力早把结果跑出来了。
想自己跑一遍这个程序的,等上半分钟吧。
试题 D: 骰子制造
本题总分: 10 10 10 分
【问题描述】
骰子是游戏中常用的一个工具,骰子是一个正六面体,六个面分别是 1 1 1 到 6 6 6 点,每种一个,通常情况下, 1 1 1 到 6 6 6 点的样子如下图所示。
其中 1 1 1、 4 4 4、 5 5 5 点旋转 90 90 90、 180 180 <