第一题

计算自然数 \(n\) 至自然数 \(m\) 之间(含 \(n\)\(m\))所有整数中的各位数字是奇数的和。如输入 \(n\)\(110\)\(m\)\(118\),则结果为 \(1+1+1+1+1+1+1+1+1+3+1+1+1+1+5+1+1+1+1+7+1+1=34\)

签到题,循环模拟即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class p1 {

public static void main(String[] args) {
Scanner sc = new Scanner(System.in);

int n, m;
int ans = 0;

n = sc.nextInt();
m = sc.nextInt();
sc.close();

p1 p = new p1();

for (int i = n; i <= m; i++) {
ans += p.oddAdd(i);
}

System.out.println(ans);
}

public int oddAdd(int x) {
int i = 0;
while (x > 0) {
if ((x % 10) % 2 == 1)
i += x % 10;
x = x / 10;
}
return i;
}
}

第二题

当你输入信用卡号码的时候,有没有担心输错了而造成损失呢?其实可以不必这么担心,因为并不是一个随便的信用卡号码都是合法的,它必须通过Luhn算法来验证通过。

该校验的过程:

  1. 从卡号最后一位数字开始,逆向将奇数位(\(1, 3, 5\) 等等)相加。
  2. 从卡号最后一位数字开始,逆向将偶数位数字,先乘以 \(2\)(如果乘积为两位数,则将其减去 \(9\)),再求和。
  3. 将奇数位总和加上偶数位总和,结果应该可以被 \(10\) 整除。

例如,卡号是:5432123456788881

则奇数、偶数位(用斜体标出)分布:5 4 3 2 1 2 3 4 5 6 7 8 8 8 8 1

奇数位和 \(= 35\)

偶数位乘以 \(2\)(有些要减去 \(9\))的结果:\(1\ 6\ 2\ 6\ 1\ 5\ 7\ 7\),求和 \(= 35\)

最后 \(35+35=70\) 可以被 \(10\) 整除,认定校验通过。

请编写一个程序,从键盘输入卡号,然后判断是否校验通过。通过显示:“成功”,否则显示“失败”。

比如,用户输入:356827027232780

程序输出:成功

签到题,分奇偶循环模拟即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class p2 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String str = sc.nextLine();
sc.close();

int sum = 0;
for (int i = str.length() - 1; i >= 0; i -= 2) {
sum += Integer.parseInt(str.charAt(i) + "");
}

for (int i = str.length() - 2; i >= 0; i -= 2) {
int temp = Integer.parseInt(str.charAt(i) + "") * 2;
sum += temp > 9 ? temp - 9 : temp;
}

if (sum % 10 == 0)
System.out.println("成功");
else
System.out.println("失败");
}
}

第三题

整数的分划问题。

如,对于正整数 \(n=6\),可以分划为:

\(6\)

\(5+1\)

\(4+2, 4+1+1\)

\(3+3, 3+2+1, 3+1+1+1\)

\(2+2+2, 2+2+1+1, 2+1+1+1+1\)

\(1+1+1+1+1+1+1\)

现在的问题是,对于给定的正整数 \(n\),编写算法打印所有划分。

用户从键盘输入 \(n\) (范围 \(1 \sim 10\)

程序输出该整数的所有划分。

递归枚举入门题,关于重复的情况我是做了一个判断,比较当前整数剩余部分和上一个值的大小,循环从较小值开始倒序的话,就不会重复了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class p3 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
sc.close();

p3 p = new p3();

String ans = "";
p.divide(n, n, ans);
}

public void divide(int n, int prev, String ans) {
if (n == 0)
System.out.println(ans);
else {
for (int i = prev < n ? prev : n; i > 0; i--) {
if (ans.isEmpty()) {
divide(n - i, i, ans + i);
} else {
divide(n - i, i, ans + "+" + i);
}
}
}
}
}

第四题

给定一个长度为 \(n\) 的正整数数列 \(A_1, A_2, \dots, A_n\) 和一个非负整数 \(x\),给定 \(m\) 次查询,每次询问能否从区间 \([k, r]\) 中选择两个数使得它们的异或等于 \(x\)

输入格式:

输入第一行包含三个整数 \(n, m, x\)

第二行包含 \(n\) 个正整数 \(A_1, A_2, \dots, A_n\)

接下来 \(m\) 行,每行两个整数 \(k, r\) 表示询问区间 \([k, r]\)

输出格式:

对于每个查询,如果该区间内存在两个数的异或为 \(x\) 则输出 yes,否则输出 no

输入样例:

4 4 1

1 2 3 4

1 4

1 2

2 3

3 3

输出样例:

yes

no

yes

no

难点在于异或,Java我不记得有现成方法,那么就得手动实现,寻找的时候做双循环,判断一下一致性就行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
public class p4 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int m = sc.nextInt();
int x = sc.nextInt();

p4 p = new p4();

int[] array = new int[n];

for (int i = 0; i < n; i++) {
array[i] = sc.nextInt();
}

for (int i = 0; i < m; i++) {
int a = sc.nextInt() - 1;
int b = sc.nextInt() - 1;
boolean suc = false;
for (int j = a; j <= b; j++) {
for (int k = a; k <= b; k++) {
if (j != k && p.xor(array[j], array[k]) == x) {
System.out.println("yes");
suc = true;
break;
}
}
if (suc)
break;
}

if (!suc) {
System.out.println("no");
}
}
sc.close();
}

public int xor(int a, int b) {
int i = 0;
int sum = 0;
while (a > 0 || b > 0) {
if (a % 2 == 0 && b % 2 == 1 || a % 2 == 1 && b % 2 == 0)
sum += Math.pow(2, i);
a /= 2;
b /= 2;
i++;
}
return sum;
}
}

第五题

学校大门外长度为 \(L\) 的马路上有一排树,每两棵相邻的树之间的间隔都是 \(1\) 米。可以把马路看成一个数轴,马路的一端在数轴 \(0\) 的位置,另一端在 \(L\) 的位置,数轴上的每个整数点(即 \(0, 1, 2, \dots, L\))都有一棵树。由于马路上有一些区域要用来建地铁。这些区域用它们在数轴上的起始点和终止点表示。已知任意区域的起始点和终止点的坐标都是整数,区域之间可能有重合的部分。现在要把这些区域中的树(包括区域端点处的两棵树)移走。请计算将这些树都移走后,马路上还有多少棵树。

输入

500 3

150 300

100 200

470 471

输出

298

(表中输入数据说明:第一行第一个表示区间的总长度,第二个数表示有几个需要移动的区域:例如 500 3,表示区间的总长度为 \([0, 500]\),需要移动的是 \(3\) 个区间,这三个区间分别是 \([150, 300]\)\([100, 200]\)\([470, 471]\)

比较巧妙的一题,不过也是模版了。

可能有人想到如下的做法:

使用一个可以覆盖所有区间范围的数组,对每个区间进行标记,结果为数组中被标记元素的个数。这种方法的时间复杂度是 \(O(nm)\)。 注:\(n\) 是区间个数,\(m\) 是所有区间总的范围。

但这样的话,时间复杂度其实比较低了,一种更优的思路如下:

使用一个可以覆盖所有区间范围的数组 flg,初始化时将数组中的元素都置为 \(0\)。对于每一个区间 \([l, r]\),将 flg[l]++flg[r+1]--。最后使用一个累加器 cnt,初始置为 \(0\)。依次扫描数组中的每一个元素,对于第 \(i\) 个元素,cnt += flg[i]。此时,若 cnt > 0,则说明 \(i\) 在某些区间中;若 cnt == 0,则证明 \(i\) 不在任何区间中。统计 cnt == 0 的元素个数即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class p5 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int l = sc.nextInt();
int n = sc.nextInt();

int cnt = 0, ret = 0;
int[] flg = new int[l+1];

for (int i = 0; i < n; i++) {
int a = sc.nextInt();
int b = sc.nextInt();
flg[a]++;
flg[b + 1]--;
}
for (int i = 0; i <= l; i++) {
cnt += flg[i];
if (cnt == 0) {
ret++;
}
}

System.out.println(ret);
sc.close();
}
}

第六题

一种简单的文件传输加密系统:假设明文一共有 \(m\) 个字节,加密后的密文第 \(1\) 字节和第 \(m\) 字节互换位置,第 \(2\) 字节和第 \(m-2\) 字节互换位置,第 \(3\) 字节和第 \(m-3\) 字节互换位置,以此类推。互换位置后,每一个字节的第 \(1\) 和第 \(8\) 比特,第 \(2\) 和第 \(7\) 比特,第 \(3\) 和第 \(6\) 比特,第 \(4\) 和第 \(5\) 比特互换位置。为了验证程序正确,输入一个字符串,加密一次后以整数格式输出每个字符,再加密一次后,以整数格式和字符串格式输出第二次加密的内容。

测试用例:

输入“abcde”,则运行结果如下:

edcba

-90 38 -58 70 -122

abcde

97 98 99 100 101

这题其实有点迷惑人,关键在于他题目描述的不清不楚,由于我写这道题的时候时间不多了,所以直接放弃掉了最后一题,考虑到大概率是人工阅卷,这题就按照规范一点的写法完成了。

难点在于对二进制位的处理,这里我直接按照计算逻辑硬写了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
public class p6 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String str = sc.nextLine();
sc.close();

p6 p = new p6();

String newStr = p.stringReverse(str);
for (int i = 0; i < newStr.length(); i++) {
short temp = (short) newStr.charAt(i);
System.out.print(temp + " ");
}
System.out.println();
String newnewStr = p.stringReverse(newStr);
System.out.println(newnewStr);
for (int i = 0; i < newnewStr.length(); i++) {
short temp = (short) newnewStr.charAt(i);
System.out.print(temp + " ");
}
}

public short bitReverse(int n) {
short i = 7;
short sum = 0;
boolean minus;
if (n % 2 == 1)
minus = true;
else
minus = false;
n /= 2;
i--;
while (n > 0) {
if (n % 2 == 1)
sum += Math.pow(2, i);
n /= 2;
i--;
}
if (minus)
sum -= 128;
return sum;
}

public String stringReverse(String str) {
String newStr = new String();
for (int i = str.length() - 1; i >= 0; i--) {
short tempInt = bitReverse(str.charAt(i));
char temp = (char) tempInt;
newStr += temp;
}
return newStr;
}
}

第七题

小蓝学习了最短路径之后特别高兴,他定义了一个特别的图,希望找到图中的最短路径。

小蓝的图由 \(2021\) 个结点组成,依次编号 \(1\)\(2021\)

对于两个不同的结点 \(a, b\),如果 \(a\)\(b\) 的差的绝对值大于 \(21\),则两个结点之间没有边相连;如果 \(a\)\(b\) 的差的绝对值小于等于 \(21\),则两个点之间有一条长度为 \(a\)\(b\) 的最小公倍数的无向边相连。

例如:结点 \(1\) 和结点 \(23\) 之间没有边相连;结点 \(3\) 和结点 \(24\) 之间有一条无向边,长度为 \(24\);结点 \(15\) 和结点 \(25\) 之间有一条无向边,长度为 \(75\)

请计算,结点 \(1\) 和结点 \(2021\) 之间的最短路径长度是多少。

这题眼熟,我拿蓝桥杯国二那年,省赛填空题压轴就是这个,这里就不给题解了,各位想了解的话直接看往年题解就行了。