> 文档中心 > 蓝桥杯31天冲刺打卡题解(Day8)

蓝桥杯31天冲刺打卡题解(Day8)


Day8

第一题

第五届2014年蓝桥杯省赛

神奇算式

JavaA组第3题

填空题

利用Set集合去重,并判断set的大小是否为4

import java.util.Arrays;import java.util.HashSet;import java.util.Set;public class Main {    public static void main(String[] args) { int cnt = 0; for (int i = 1; i <= 987; i++) {     for (int j = i; j <= 987; j++) {  int res = i * j;  if (res >= 10000) continue;  String s1 = i + "" + j;  String s2 = res + "";  char[] c1 = s1.toCharArray();  char[] c2 = s2.toCharArray();  if (check(c1, c2)) cnt++;     } } System.out.println(cnt);    }    private static boolean check(char[] c1, char[] c2) { Arrays.sort(c1); Arrays.sort(c2); String s1 = String.valueOf(c1); String s2 = String.valueOf(c2); if (s1.equals(s2) && check_reduplicate(s2)) {     return true; } return false;    } // 判重    private static boolean check_reduplicate(String s) { Set<String> set = new HashSet<>(); for (int i = 0; i < s.length(); i++) { set.add(s.charAt(i) + ""); } if (set.size() == 4) {     return true; } return false;    }}

第二题

第九届2018年蓝桥杯省赛

缩位求和

JavaC组第6题

简单的字符串处理问题。

import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner sc = new Scanner(System.in);String a = sc.next();while (a.length() > 1) {int sum = 0;for (int i = 0; i < a.length(); i++) {sum += a.charAt(i) - '0'; // 转为int类型}String b = sum + ""; // 转为String类型a = b; // 重新赋值 再次拆分}System.out.println(a); // 此时a只剩一位}}

第三题

NOIP2013提高组

积木大赛

贪心+差分

给定一个区间 [ L , R ] [L, R] [L,R],每次操作可以使这个区间所有积木数+1,现在就是给我们一个大厦的宽度n以及每1个宽度需要的积木数,让我们求最少操作多少次可以建成大厦,不难看出是一道贪心题目,使我们的区间尽量大。

我们可以把过程逆过来,因为我们已经知道了每堆积木的高度,所以我们可以将某一段区间所有高度-1,求我们操作多少次可以把所有高度变为0

看第一个样例:

操作所有数,全部-1

image-20220315193023626

再操作前3个数:

image-20220315193123659

再操作第2、3个数:

image-20220315193200812

然后再分别操作第3个数和第5个数,总操作数为5

image-20220315193259672

我们试着用差分来解决这道题,将原数组 h i h_i hi 变为差分数组 b i b_i bi = > b i = h i − hi−1 => b_i = h_i - h_{i - 1} =>bi=hihi1

原数组: h 1 , h 2 . . . h 3 h_1, h_2 ... h_3 h1,h2...h3

差分数组:

b 1 = h 1 b_1 = h_1 b1=h1

b 2 = h 2 − h 1 b_2 = h_2 - h_1 b2=h2h1

. . . ... ...

b n = h n − hn−1 b_n = h_n - h_{n-1} bn=hnhn1

bn+1 = − h n b_{n+1} = -h_n bn+1=hn

当我们想让区间 [ L , R ] [L, R] [L,R]的所有数 h L , hL+1 . . . h R h_L,h_{L+1}...h_R hL,hL+1...hR 全部-1,我们只需要改差分数组的两个值就可以, b L = h L − hL−1 b_L = h_L-h_{L-1} bL=hLhL1 hL−1 h_{L-1} hL1不变, h L h_{L} hL 减了1,所以 b L b_{L} bL 减了1; bL+1 = hL+1 − h L b_{L+1} = h_{L+1}-h_{L} bL+1=hL+1hL hL+1 h_{L+1} hL+1 h L h_{L} hL 都减了1,差值不变,所以 bL+1 b_{L+1} bL+1 不变,以此类推剩下的都不变,但当 bR+1 = hR+1 − h R b_{R + 1} = h_{R + 1} - h_R bR+1=hR+1hR 时, hR+1 h_{R + 1} hR+1 不变, h R h_R hR 减了1,所以是让 bR+1 b_{R + 1} bR+1 加了1。

综上,如果对原数组的区间 [ L . R ] [L. R] [L.R]所有值全部减1,那么对应到差分数组我们只改变了两个值:一个是 b L b_L bL 减1,另一个是 bR+1 b_{R + 1} bR+1 加1。

原数组任意的区间操作都可以对应到差分数组两个数的操作,而且是一一对应的。

我们要使原数组所有数变成0,那么差分数组也全部变成0,相当于我们每次要从差分数组 b 1 , b 2 . . . bn+1 b_1, b_2 ... b_{n + 1} b1,b2...bn+1 挑两个数,前一个减1,后一个加1,求最少操作多少次可以将所有数变成0。

我们每一次从前往后看,找到第一个大于0的 b i b_i bi,我们从后面任意找一个负数 b j b_j bj b i b_i bi 配对,然后让 b i b_i bi 减1, b j b_j bj 加1即可。

image-20220315203121506

我们如何确定我们一定能在 b i b_i bi 后面找到一个负数呢?

image-20220315204145655

把所有 b i b_i bi 拆分成原数组,所有项全部都消掉了,只剩下了 − hi−1 -h_{i - 1} hi1

我们发现:因为所有 h i ⩾ 0 h_i \geqslant 0 hi0,所以 b i + . . . + bn+1 b_i+ ...+b_{n + 1} bi+...+bn+1 一定是小于等于0的,所以当 b i > 0 b_i > 0 bi>0 时, bi+1 + . . . + bn+1 b_{i+1}+ ...+b_{n + 1} bi+1+...+bn+1 一定是严格小于0的,所以 b i b_i bi 后面必然存在一个负数。

综上,证明完毕。

求出差分数组并扫描一遍即可。

时间复杂度 O ( N ) O(N) O(N)

import java.util.Scanner;public class Main { static final int N = 100010;    static int[] h = new int[N]; // 原数组    static int[] b = new int[N]; // 差分数组    public static void main(String[] args) {Scanner sc = new Scanner(System.in); int n = sc.nextInt(); for (int i = 1; i <= n; i++) h[i] = sc.nextInt();  for (int i = 1; i <= n + 1; i++) b[i] = h[i] - h[i - 1]; // 差分数组 i要取到n + 1  int res = 0; for (int i = 1; i <= n + 1; i++) {     if (b[i] > 0) res += b[i]; // 只要b[i] > 0 我们就至少需要操作一次 } System.out.println(res);}}

简化版代码:如果当前数大于前一个数,则 res += 当前数 - 前一个数

import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner sc = new Scanner(System.in); int n = sc.nextInt(); int res = 0, last = 0; // 记录上一次的积木数 while (n-- > 0) {     int h = sc.nextInt();     if (h > last) res += h - last; // 如果h较大,则证明h搭积木的时候可以带着前一个last一起搭     last = h; } System.out.println(res);}}