蓝桥杯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

蓝桥杯31天冲刺打卡题解(Day8)_第1张图片

再操作前3个数:

蓝桥杯31天冲刺打卡题解(Day8)_第2张图片

再操作第2、3个数:

蓝桥杯31天冲刺打卡题解(Day8)_第3张图片

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

蓝桥杯31天冲刺打卡题解(Day8)_第4张图片

我们试着用差分来解决这道题,将原数组 h i h_i hi 变为差分数组 b i b_i bi = > b i = h i − h i − 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 − h n − 1 b_n = h_n - h_{n-1} bn=hnhn1

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

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

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

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

我们要使原数组所有数变成0,那么差分数组也全部变成0,相当于我们每次要从差分数组 b 1 , b 2 . . . b n + 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即可。

蓝桥杯31天冲刺打卡题解(Day8)_第5张图片

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

蓝桥杯31天冲刺打卡题解(Day8)_第6张图片

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

我们发现:因为所有 h i ⩾ 0 h_i \geqslant 0 hi0,所以 b i + . . . + b n + 1 b_i+ ...+b_{n + 1} bi+...+bn+1 一定是小于等于0的,所以当 b i > 0 b_i > 0 bi>0 时, b i + 1 + . . . + b n + 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);
	}
}

你可能感兴趣的:(#,刷题笔记,蓝桥杯,java,算法,NOIP,职场和发展)