第14届蓝桥杯JavaB组省赛部分题解

重新做了一下题,过了该网站的评测

A:阶乘求和

令S= 1! + 2! + 3! +…+ 202320232023!,求S的末尾9位数字。提示:答案首位不为0.

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.math.BigInteger;
/***
 * 求 1! + 2! + 3! + ... + 202320232023! 的后九位数
 * 
 * 当阶乘为40时,后面的九位数全部为0, 不会再变化
*/
public class Main {
	static BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
	static PrintWriter out = new PrintWriter(System.out);
	static long N = 202320232023l;
	static BigInteger ans = new BigInteger(String.valueOf(1));
	static BigInteger temp = new BigInteger(String.valueOf(1));

	static void mutiply(BigInteger a) {
		temp = temp.multiply(a);
	}

	static void add() {
		ans = ans.add(temp);
	}

	static boolean check(String s) {
		int len = s.length() - 1, cnt = 0;
		for (; len >= 0; len--) {
			if (s.charAt(len) == '0')
				cnt++;
			else
				return cnt == 9;
		}
		return cnt == 9;
	}

	static BigInteger get(int x) {
		BigInteger a = BigInteger.ONE;
		for (int i = 2; i <= x; i++) {
			a = a.multiply(new BigInteger(String.valueOf(i)));
		}
		return a;
	}

	public static void main(String[] args) throws IOException {
		for (long i = 2; i <= 40; i++) {
			mutiply(new BigInteger(String.valueOf(i)));
			out.printf("%50s  ", temp);
			add();
			out.println(ans);
		}
		String s = ans.toString(10);
		int len = s.length();
		out.print(s.substring(len - 9, len));
		in.close();
		out.close();
	}
}

B: 幸运数字

哈沙德数是指在某个固定的进位制当中,可以被各位数字之和整除的正整数。例如126是十进制下的一个哈沙德数,因为(126)10mod(1+2+6) = 0;126也是八进制下的哈沙德数,因为(126)10= (176)8,(126)10mod(1 + 7 + 6) = 0;同时126也是16进制下的哈沙德数,因为(126)10= (7e)16,(126)10mod(7 + e) = 0。
小蓝认为,如果一个整数在二进制、八进制、十进制、十六进制下均为哈沙德数,那么这个数字就是幸运数字,第1至第10个幸运数字的十进制表示为:1,2,4,6,8,40,48,72,120,126…。现在他想知道第2023个幸运数字是多少?你只需要告诉小蓝这个整数的十进制表示即可

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.math.BigInteger;

public class Main {
	static BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
	static PrintWriter out = new PrintWriter(System.out);
	static int N = (int) 1e5 + 10;
	static BigInteger cur = BigInteger.ONE;

	static boolean check(int x) {
		String binary = Integer.toBinaryString(x); // 2
		String o = Integer.toOctalString(x); // 8
		String hex = Integer.toHexString(x); // 16
		String t = Integer.toString(x);
		return lucky(binary, x) && lucky(o, x) && lucky(hex, x) && lucky(t, x);
	}

	static boolean lucky(String s, int x) {
		int mod = 0;
		for (char c : s.toCharArray()) {
			mod += get(c);
		}
		if (x % mod != 0)
			return false;
		return true;
	}

	static int get(char c) {
		if (c >= '0' && c <= '9')
			return c - '0';
		else
			return c - 'a' + 10;
	}

	public static void main(String[] args) throws IOException {

		for (int i = 1, k = 0; k < 2023; i++) {
			if (check(i)) {
				out.println(i);
				k++;
			}
		}

		in.close();
		out.close();
	}
}

C: 数组分割

小蓝有一个长度为N的数组A= [A0;A1;…;AN−1]。现在小蓝想要从A对应的数组下标所构成的集合I={0;1;2;…;N−1}中找出一个子集R1,那么R1在I中的补集为R2。记S1=∑r∈R1Ar,S2=∑r∈R2Ar,我们要求S1和S2均为偶数,请问在这种情况下共有多少种不同的R1。当R1或R2为空集时我们将S1或S2视为0。

  • 当总和为奇数的时候,无论怎么选取,总会有S1 或者 S2 为奇数
  • 统计奇偶数的个数,偶数中可以提取任意个数 C e v e n i C^i_{even} Ceveni,奇数需要提取偶数个 C o d d 2 i C^{2i}_{odd} Codd2i
    然后利用 乘法原理 相乘即可
  • 进一步可以发现 ∑ i = 0 e v e n C e v e n i = 2 e v e n , ∑ i = 0 o d d / 2 C o d d 2 i = 2 o d d − 1 \sum^{even}_{i=0}{C^i_{even}} = 2^{even}, \sum^{odd/2}_{i=0}{C^{2i}_{odd}} = 2^{odd - 1} i=0evenCeveni=2even,i=0odd/2Codd2i=2odd1,那么答案就是 2 e v e n × 2 o d d − 1 % m o d 2^{even} \times 2^{odd-1} \%mod 2even×2odd1%mod
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
 
public class Main {
    static BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
    static PrintWriter out = new PrintWriter(System.out);
    static int mod = 1000000007;
     
    static long qmk(int x, int y) {
        long res = 1, a = x;
        while (y > 0) {
            if ((y & 1) == 1)
                res = res * a % mod;
            y >>= 1;
            a = a * a % mod;
        }
        return res;
    }
 
    static void solve() throws IOException {
        int n = Integer.parseInt(in.readLine());
        int[] a = new int[n + 1];
        String[] ss = in.readLine().split(" ");
        long sum = 0;
        int even = 0, odd = 0;
        for (int i = 0; i < n; i++) {
            a[i] = Integer.parseInt(ss[i]);
            sum += a[i];
            if (a[i] % 2 == 0)
                even++;
            else
                odd++;
        }
 
        if (sum % 2 != 0) {
            out.println(0);
        } else {
            out.println(qmk(2, even) * qmk(2, odd - 1) % mod);
        }
 
    }
 
    public static void main(String[] args) throws IOException {
        int t = Integer.parseInt(in.readLine());
        while (t-- > 0)
            solve();
        in.close();
        out.close();
    }
}

D: 矩形总面积

平面上有个两个矩形R1和R2,它们各边都与坐标轴平行。设(x1;y1)和(x2;y2)依次是R1的左下角和右上角坐标,(x3;y3)和(x4;y4)依次是R2的左下角和右上角坐标,请你计算R1和R2的总面积是多少?注意:如果R1和R2有重叠区域,重叠区域的面积只计算一次。

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
/***
 * 思路:
 * 1. 计算两个矩阵的面积之和
 * 2. 减去重叠部分
*/
public class D {
	static BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
	static PrintWriter out = new PrintWriter(System.out);

	public static void main(String[] args) throws IOException {
		String[] s = in.readLine().split(" ");
		int x1 = Integer.parseInt(s[0]), y1 = Integer.parseInt(s[1]), x2 = Integer.parseInt(s[2]),
				y2 = Integer.parseInt(s[3]), x3 = Integer.parseInt(s[4]), y3 = Integer.parseInt(s[5]),
				x4 = Integer.parseInt(s[6]), y4 = Integer.parseInt(s[7]);
		long h1 = Math.abs(x2 - x1), w1 = Math.abs(y2 - y1), h2 = Math.abs(x4 - x3), w2 = Math.abs(y4 - y3); //计算出矩阵的长宽
		long ans = h1 * w1 + h2 * w2; //总面积
		long maxx = Math.max(Math.max(x1, x3), Math.max(x2, x4)), //求出最大的x坐标
			 minx = Math.min(Math.min(x1, x3), Math.min(x2, x4)); // 求出最小的x坐标
		long maxy = Math.max(Math.max(y1, y3), Math.max(y2, y4)),  //求出最大的y坐标
		     miny = Math.min(Math.min(y1, y3), Math.min(y2, y4)); // 求出最小的y坐标
		long rh = Math.max(0, h1 + h2 - Math.abs(maxx - minx)),  
			 rw = Math.max(0, w1 + w2 - Math.abs(maxy - miny)); //求出重叠矩形的长宽,
		ans -= rh * rw;
		out.println(ans);
		in.close();
		out.close();
	}
}


E:蜗牛

这天,一只蜗牛来到了二维坐标系的原点。在x轴上长有n根竹竿。它们平行于y轴,底部纵坐标为0,横坐标分别为x1;x2;…;xn。竹竿的高度均为无限高,宽度可忽略。蜗牛想要从原点走到第n个竹竿的底部也就是坐标(xn;0)。它只能在x轴上或者竹竿上爬行,在x轴上爬行速度为1单位每秒;由于受到引力影响,蜗牛在竹竿上向上和向下爬行的速度分别为0.7单位每秒和1.3单位每秒。为了快速到达目的地,它施展了魔法,在第i和i+ 1根竹竿之间建立了传送门(0<i<n),如果蜗牛位于第i根竹竿的高度为ai的位置(xi;ai),就可以瞬间到达第i+ 1根竹竿的高度为bi+1的位置(xi+1;bi+1),请计算蜗牛最少需要多少秒才能到达目的地。

一开始想得是建图,但是代码运行错误QAQ

  • 可以用动态规划,因为每个杆子有3种状态:杆子底部,杆子上的传送阵,杆子上被传送的地方
  • 每一个位置都可以从前面一个杆子转移过来
  • 只要每一步取到最优解,那么全局必定是最优解
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.Arrays;
 
public class Main {
    static BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
    static PrintWriter out = new PrintWriter(System.out);
    static int N = (int) 1e5 + 10, INF = 0x3f3f3f3f;
    static double[][] dp = new double[N][3];
    static int[] d = new int[N], a = new int[N], b = new int[N];
    static double x = 1, up = 0.7, down = 1.3;
    static {
        for (int i = 0; i < N; i++)
            Arrays.fill(dp[i], INF);
    }
    
    public static void main(String[] args) throws IOException {
        int n = Integer.parseInt(in.readLine());
        String[] s = in.readLine().split(" ");
        for (int i = 1; i <= n; i++) {
            d[i] = Integer.parseInt(s[i - 1]);
        }
        for (int i = 1; i < n; i++) {
            s = in.readLine().split(" ");
            a[i] = Integer.parseInt(s[0]);
            b[i + 1] = Integer.parseInt(s[1]);
        }
        // 0表示到达杆子的底部,1表示到达杆子的传送阵,2表示到达传送的地方
        dp[1][0] = d[1] / x;
        dp[1][1] = d[1] / x + a[1] / up;
 
        for (int i = 2; i <= n; i++) {
            int dis = d[i] - d[i - 1];
            // 到达底部有: 从前面一个杆子底部走,从前一个杆子传送过来后下来
            dp[i][0] = Math.min(dp[i - 1][0] + dis / x, dp[i - 1][1] + b[i] / down);
            // 到达传送阵有: 从前一个杆子底部走然后上去,从前一个杆子传送然后下/上
            dp[i][1] = Math.min(dp[i - 1][0] + dis / x + a[i] / up,
                    dp[i - 1][1] + (b[i] > a[i] ? (b[i] - a[i]) / down : (a[i] - b[i]) / up));
            // 到达传送的地方有: 从前一个传送阵传送,从前一个杆子的底部走过来上去
            dp[i][2] = Math.min(dp[i - 1][1], dp[i - 1][0] + dis / x + b[i] / up);
        }
 
        out.printf("%.2f", Math.round(dp[n][0] * 100) / 100d);
        in.close();
        out.close();
    }
}

F: 合并区域

小蓝在玩一款种地游戏。现在他被分配给了两块大小均为N×N的正方形区域。这两块区域都按照N×N的规格进行了均等划分,划分成了若干块面积相同的小区域,其中每块小区域要么是岩石,要么就是土壤,在垂直或者水平方向上相邻的土壤可以组成一块土地。现在小蓝想要对这两块区域沿着边缘进行合并,他想知道合并以后可以得到的最大的一块土地的面积是多少(土地的面积就是土地中土壤小区域的块数)?
在进行合并时,小区域之间必须对齐。可以在两块方形区域的任何一条边上进行合并,可以对两块方形区域进行90度、180度、270度、360度的旋转,但不可以进行上下或左右翻转,并且两块方形区域不可以发生重叠。

  • 模拟:选定一个较大的二维矩阵,然后一块放中间,另外一块进行合并,然后选定位置进行BFS
import java.io.*;
import java.util.LinkedList;
import java.util.Queue;

public class Main {
    static BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
    static PrintWriter out = new PrintWriter(System.out);
    static int N = 55, M = N * 3, n;
    static int[][] a = new int[M][M], b = new int[N][N];
    static int[] d = { 0, -1, 0, 1, 0 };
    
    static int bfs(int[][] mp, int left, int right, int top, int bottom) {
        int res = 0;
        boolean[][] st = new boolean[M][M];
        for (int i = top; i <= bottom; i ++) {
            for (int j = left; j <= right; j ++) {
                if (mp[i][j] == 1 && !st[i][j]) {
                    int cnt = 0;
                    Queue<int[]> q = new LinkedList<>();
                    q.add(new int[] { i, j });
                    while (!q.isEmpty()) {
                        int[] cur = q.poll();
                        int tx = cur[0], ty = cur[1];
                        if (st[tx][ty]) continue;
                        st[tx][ty] = true;
                        cnt ++;
                        for (int k = 0; k < 4; k++) {
                            int dx = tx + d[k], dy = ty + d[k + 1];
                            if (dx < top || dx > bottom || dy < left || dy > right || mp[dx][dy] == 0 || st[dx][dy]) continue;
                            q.add(new int[] { dx, dy });
                        }
                    }
                    res = Math.max(res, cnt);
                }
            }
        }
        return res;
    }
		//旋转
    static int[][] rotate(int[][] mp) {
        int[][] res = new int[N][N];
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n; j++) {
                res[j][n - i + 1] = mp[i][j];
            }
        }
        return res;
    }

    public static void main(String[] args) throws IOException {
        n = Integer.parseInt(in.readLine());
        for (int i = n + 1; i <= n * 2; i++) {
            String[] s = in.readLine().split(" ");
            for (int j = n + 1; j <= n *2; j++) {
                a[i][j] = Integer.parseInt(s[j - n - 1]); //放在中间的位置
            }
        }
        for (int i = 1; i <= n; i++) {
            String[] s = in.readLine().split(" ");
            for (int j = 1; j <= n; j++) {
                b[i][j] = Integer.parseInt(s[j - 1]);
            }
        }
        int ans = 0;
        for (int x = 0; x < 4; x ++) { //旋转4次,每次旋转都进行各种合并
            int[][] g = new int[M][M];
            for (int k = 2; k < 2 * n; k ++) {
                for (int i = 0; i < M; i ++) System.arraycopy(a[i], 0, g[i], 0,M);
                for (int i = 1; i <= n; i ++) {
                    for (int j = k; j <= n + k; j ++) { //上下两边进行合并
                        g[i][j] = b[i][j - k + 1];
                        g[i + 2 * n][j] = b[i][j - k + 1];
                    }
                }
                int top = 1, bottom = 2 * n, left, right;
                if (k <= n) {
                    left = k;
                    right = 2 * n;
                } else {
                    left = n + 1;
                    right = k + n - 1;
                }
                ans = Math.max(ans, bfs(g, left, right, top, bottom));
                ans = Math.max(ans, bfs(g, left, right, top + n, bottom + n));
            }
            for (int k = 2; k < 2 * n; k ++) {
                for (int i = 0; i < M; i ++) System.arraycopy(a[i], 0, g[i], 0,M); //左右两边进行合并
                for (int i = k; i <= n + k; i ++) {
                    for (int j = 1; j <= n; j ++) {
                        g[i][j] = b[i - k + 1][j];
                        g[i][j + 2 * n] = b[i - k + 1][j];
                    }
                }
                int top, bottom, left = 1,  right =  2 * n;
                if (k <= n) {
                    top = k;
                    bottom = 2 * n;
                } else {
                    top = n + 1;
                    bottom = n + k - 1;
                }
                ans = Math.max(ans, bfs(g, left, right, top, bottom));
                ans = Math.max(ans, bfs(g, left + n, right + n, top, bottom));
            }
            b = rotate(b);
        }
        out.println(ans);
        in.close();
        out.close();
    }
}

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值