第12届蓝桥杯JAVA A组刷题笔记


刷题平台 [P1551 - 蓝桥杯2021初赛] 直线 - New Online Judge (ecustacm.cn)

“蓝桥杯”练习系统 (lanqiao.cn)

2021年蓝桥杯第十二届大赛真题 - 题库 - C语言网 (dotcpp.com)

蓝桥杯javaA

eclipse快捷键及使用技巧篇

(21条消息) Eclipse使用技巧总览(基于蓝桥杯2021官方指定Java组2020-06版本)kali._的博客-CSDN博客

[(21条消息) 在eclipse中如何使用Debug进行调试_RAIN_FROM2019的博客-CSDN博客_eclipse怎么debug调试](https://blog.csdn.net/RAIN_FROM2019/article/details/90479182#:~:text=关于 eclipse 的 debug 详细步骤: 第一步:打开java代码文件,在左侧双击鼠标设置断点,或者在左侧右键点击Toggle Breakpoint。. 会看到出现了小蓝点,Eclipse 上 使用Debug 模式启动 进行 断点 调试 (新手教程).)

第12届

y总讲解视频

第十二届蓝桥杯C++ B组讲解_哔哩哔哩_bilibili

A 相乘

本题总分:5 分
【问题描述】
小蓝发现,他将 1 至 1000000007 之间的不同的数与 2021 相乘后再求除以
1000000007 的余数,会得到不同的数。
小蓝想知道,能不能在 1 至 1000000007 之间找到一个数,与 2021 相乘后
再除以 1000000007 后的余数为 999999999。如果存在,请在答案中提交这个数;
如果不存在,请在答案中提交 0。

ans : 17812964

//本用long就行,我这就当学习下大数的API使用
import java.math.BigInteger;

public class 相乘 {
	public static void main(String[] args) {
		BigInteger x1 = new BigInteger(2021 + "");
		BigInteger x3 = new BigInteger(1000000007 + "");
		BigInteger ans = new BigInteger(0 + "");
		for (int i = 1; i < 1000000007; i++) {
			BigInteger x2 = new BigInteger(i + "");
			ans = x1.multiply(x2).mod(x3);
			if (ans.intValue() == 999999999)
				System.out.println(i);
		}
	}
}
大数类常用构造方法location常用方法取模转数值
BigInteger (整数)直接传字符串java.math.BigIntegerabsadd(BigInteger x)subtractmultiplydividemodfloatValue/doubleValue
BigDecimal (浮点数)直接传字符串java.math.BigDecimalabsaddsubtractmultiplydividemodintValue/longValue

B 直线

本题总分:5 分 【问题描述】
在平面直角坐标系中,两点可以确定一条直线。如果有多点在一条直线上,
那么这些点中任意两点确定的直线是同一条。
给定平面上 2 × 3 个整点 {(x, y)|0 ≤ x < 2, 0 ≤ y < 3, x ∈ Z, y ∈ Z},即横坐标
是 0 到 1 (包含 0 和 1) 之间的整数、纵坐标是 0 到 2 (包含 0 和 2) 之间的整数
的点。这些点一共确定了 11 条不同的直线。
给定平面上 20 × 21 个整点 {(x, y)|0 ≤ x < 20, 0 ≤ y < 21, x ∈ Z, y ∈ Z},即横
坐标是 0 到 19 (包含 0 和 19) 之间的整数、纵坐标是 0 到 20 (包含 0 和 20) 之
间的整数的点。请问这些点一共确定了多少条不同的直线。

ans: 40257

//想了3种方法,发现还是只有hashset才行
import java.util.HashMap;
import java.util.HashSet;

public class 直线 {
    static int arr[] = new int[10000000];

    public static void main(String[] args) {
        int ans = 41;// 斜率0与不存在的情况
        double k = 0;
        double b = 0;
//        HashMap<Double, Double> map = new HashMap<Double, Double>();
        HashMap<Integer, Integer> map = new HashMap<>();
        HashSet<Double> set = new HashSet<Double>();
        double[][] points = new double[500][2];
        int index = 0;
        for (int i = 0; i < 21; i++) {
            for (int j = 0; j < 20; j++) {
                points[index++] = new double[]{i, j};
            }
        }
        for (int i = 0; i < 420; i++) {
            for (int j = 0; j < 420; j++) {
                // 剔除斜率0与斜率不存在
                if (i != j && points[i][1] != points[j][1] && points[i][0] != points[j][0]) {
                    k = (points[i][1] - points[j][1]) / (points[i][0] - points[j][0]);
                    b = (points[i][0] * points[j][1] - points[j][0] * points[i][1]) / (points[i][0] - points[j][0]);
                    if (!map.containsKey(b * 10000) || k != map.get(b * 10000)) {
                        map.put((int)(b * 10000), (int)(k*10000));
                        ans++;
                    }
                    set.add(k * 10000 + b);//k的范围[-19,19],b[-19*19,19*19],所以可以这样搞
                    arr[(int) (k * 10000 + b * 100) + 1000000] = 1;
                }
            }
        }
        int ans2 = 41;
        for (int x : arr) {
            if (x != 0)
                ans2++;
        }
//        for (int i = 0; i < 111111; i++) {
//            map.put(1111.111 + i,2.1 + i);
//        }
        System.out.println(map.size());//5587,找了挺久问题,刚开始以为是jvm溢出了,后来发现是put时hashmap如果key一样就会替换掉
        System.out.println(set.size() + 41);//40257,正解
        System.out.println(ans);//159641,与mapsize差距非常大
        System.out.println(ans2);//22713也是错的
    }

}

C 货物摆放

本题总分:10 分 题目描述
小蓝有一个超大的仓库,可以摆放很多货物。
现在,小蓝有n 箱货物要摆放在仓库,每箱货物都是规则的正方体。
小蓝规定了长、宽、高三个互相垂直的方向,每箱货物的边都必须严格平行于长、宽、高。
小蓝希望所有的货物最终摆成一个大的立方体。即在长、宽、高的方向上分别堆L、W、H 的货物,满足n = L × W × H。
给定n,请问有多少种堆放货物的方案满足要求。
例如,当n = 4 时,有以下6 种方案:1×1×4、1×2×2、1×4×1、2×1×2、2×2×1、4×1×1。
请问,当n = 2021041820210418 (注意有16 位数字)时,总共有多少种方案?

ans : 2430

public class 货物摆放 {
	static long[] arr = new long[10000];

	public static void main(String[] args) {
		int ans = 0;

		// 这样跑不完,超级慢
//		for (long i = 1; i <= 2021041820210418L; i++) {
//			for (long j = 1; j <= 2021041820210418L; j++) {
//				for (long k = 1; k <= 2021041820210418L / i / j; k++) {
//					long x = i * j * k;
//					if (x == 2021041820210418L)
//						ans++;
//				}
//			}
//		}

		int index = 0;
		// 优化思路:只需要先求出其所有质因数放在数组中,则i j k必然为其质因数,逐个选取,然后试除法就行
		for (long i = 1; i <= 2021041820210418L / i; i++) {
			if (2021041820210418L % i == 0) {
				arr[index++] = i;
				if (i != 2021041820210418L / i)
					arr[index++] = 2021041820210418L / i;
			}
		}

		for (int i = 0; i < index; i++) {
			for (int j = 0; j < index; j++) {
				if (2021041820210418L % (arr[i] * arr[j]) == 0)
					ans++;
			}
		}
		System.out.println(ans);
	}
}

D 路径

小蓝学习了最短路径之后特别高兴,他定义了一个特别的图,希望找到图中的最短路径。
小蓝的图由2021 个结点组成,依次编号1 至2021。
对于两个不同的结点a, b,如果a 和b 的差的绝对值大于21,则两个结点之间没有边相连;
如果a 和b 的差的绝对值小于等于21,则两个点之间有一条长度为a 和b 的最小公倍数的无向边相连。
例如:结点1 和结点23 之间没有边相连;结点3 和结点24 之间有一条无向边,长度为24;
结点15 和结点25 之间有一条无向边,长度为75。
请计算,结点1 和结点2021 之间的最短路径长度是多少。

ans: 10266837

//dp
import java.util.Arrays;

public class 路径 {
	public static void main(String[] args) {
		int[] dp = new int[2100];// 存储从1到i的最短路径
		Arrays.fill(dp, Integer.MAX_VALUE);
		dp[1] = 0;
		for (int i = 2; i <= 2021; i++) {
			for (int j = 1; j < 22 && (i - j) > 0; j++) {
				dp[i] = Math.min(dp[i], dp[i - j] + lcm(i, i - j));
			}
		}
		System.out.println(dp[2021]);
	}

    //求最大公约数(greatest common divisor)
	public static int gcd(int x, int y) {
		return y == 0 ? x : gcd(y, x % y);
	}

    //求最小公倍数(least common multiple)
	public static int lcm(int x, int y) {
		return x * y / gcd(x, y);
	}
}

E 回路计数

15分		题目描述
蓝桥学院由21 栋教学楼组成,教学楼编号1 到21。
对于两栋教学楼a 和b,当a 和b 互质时,a 和b 之间有一条走廊直接相连,两个方向皆可通行,否则没有直接连接的走廊。
小蓝现在在第一栋教学楼,他想要访问每栋教学楼正好一次,最终回到第一栋教学楼(即走一条哈密尔顿回路),请问他有多少种不同的访问方案?
两个访问方案不同是指存在某个i,小蓝在两个访问方法中访问完教学楼i 后访问了不同的教学楼。

ans: 881012367360

这道题直接dfs,时间复杂度大概n!,21! 数值超大,大概按10^6一秒计时,跑完不知道到啥时候了,反正我没跑出来。这里要注意,结果很大,要用long存

//直接dfs,超费时,跑不动
public class 回路计数 {
    static long ans = 0;
    static int cnt = 0;// 记录有几个教学楼已访问

    public static void main(String[] args) {
        int[][] hasPath = new int[22][22];// 建图,表示i j 教学楼之间是否有路
        int[] statement = new int[22];// 表示i教学楼是否已被访问
        long ans = 0;
        for (int i = 1; i < 22; i++)
            for (int j = 1; j < 22; j++){
                // 互质就是最大公因数为1
                if (i != j && gcd(i, j) == 1)
                    hasPath[i][j] = 1;
            }
        dfs(hasPath, 1, 1, statement);
        System.out.println(ans);
    }

    // i表示出发地,k表示目的地(下一步)
    public static void dfs(int[][] hasPath, int i, int k, int[] statement) {
        if ((i != 1 && cnt != 21 && k == 1))
            return;
        if (i != 1 && k == 1) {
            ans++;
            return;
        }
        for (int j = 1; j < 22; j++) {
            if (hasPath[k][j] == 1 && k != j && statement[j] == 0) {
                statement[j] = 1;// j已被访问
                cnt++;
                dfs(hasPath, k, j, statement);
                statement[j] = 0;
                cnt--;
            }
        }
    }

    public static int gcd(int x, int y) {
        return y == 0 ? x : gcd(y, x % y);
    }
}
//状态压缩优化,大致意思是这样滴,如果你先走过了 2345最后到头, 然后回溯另一种情况先走了 2435然后一直到头,很明显,第二种情况后面的路径个数与前者相同,那么我们如果再去dfs就会浪费大量的时间,所以我们不妨每次dfs返回前缀为某某时后续的路径情况数量,并将其用哈希表存储,每次查到当前走过的路径前缀(不看顺序,但要明确最后一步走到哪)相同,就直接返回不必dfs
//这个很快就跑完了
import java.util.HashMap;

public class 回路计数 {
	static long ans = 0;
	static int cnt = 0;// 记录有几个教学楼已访问
	static long r = 0; // 记录所以已访问的前缀,移位操作
	static HashMap<String, Long> map = new HashMap<>();

	public static void main(String[] args) {
		int[][] hasPath = new int[22][22];// 建图,表示i j 教学楼之间是否有路
		int[] statement = new int[22];// 表示i教学楼是否已被访问
		long ans = 0;
		for (int i = 1; i < 22; i++)
			for (int j = 1; j < 22; j++) {
				// 互质就是最大公因数为1
				if (i != j && gcd(i, j) == 1)
					hasPath[i][j] = 1;
			}
		ans = dfs(hasPath, 1, 1, statement);
		System.out.println(ans);
	}

	// i表示出发地,k表示目的地(下一步)
	public static long dfs(int[][] hasPath, int i, int k, int[] statement) {
		if ((i != 1 && cnt != 21 && k == 1))
			return 0;// 表示不合法路径,没有走完就回1
		// 一直走到1的路径
		if (i != 1 && cnt == 21 && k == 1)
			return 1;
		long res = 0;
		for (int j = 1; j < 22; j++) {
			if (hasPath[k][j] == 1 && k != j && statement[j] == 0) {
				statement[j] = 1;// j已被访问
				cnt++;
				r |= (1 << j);
				long m = 0;// 记录当前前缀下,后续的路径种类
				if (!map.containsKey(r + "-" + j)) {
					m = dfs(hasPath, k, j, statement);
					map.put(r + "-" + j, m);// 卡在这里很久,没找到原因,最后才发现这里要指明最后一步是几号,不然可能前缀一样但最后一步不一样,
				} else {
					m = map.get(r + "-" + j);
				}
				statement[j] = 0;
				r ^= (1 << j);
				cnt--;
				res += m;
			}
		}
		return res;
	}

	public static int gcd(int x, int y) {
		return y == 0 ? x : gcd(y, x % y);
	}
}

后来发现一个好文 (21条消息) 【状压DP】哈密顿回路问题_鱼竿钓鱼干的博客-CSDN博客

F 最少砝码

时间限制: 1.0s 内存限制: 512.0MB 本题总分:15 分
【问题描述】
你有一架天平。现在你要设计一套砝码,使得利用这些砝码可以称出任意
小于等于 N 的正整数重量。
那么这套砝码最少需要包含多少个砝码?
注意砝码可以放在天平两边。

好文: 第十二届蓝桥杯省赛-最少砝码C+±Dotcpp编程社区

这个思路讲得挺好的 (23条消息) 第十二届蓝桥杯Java组真题-最少砝码-简单而高逼格的“对称三进制”_一个老蒟蒻的博客-CSDN博客](https://blog.csdn.net/qq_30347475/article/details/123931303)

import java.util.Scanner;

public class Main {
	public static void main(String[] args) {
		int n = 1, x, total = 1, weight = 1;// 需要的个数也就是平衡三进制位数
		Scanner sc = new Scanner(System.in);
		x = sc.nextInt();
		sc.close();
		while (total < x) {
            weight *= 3;
            total += weight;
			n++;
		}
		System.out.println(n);
	}
}

下面这个是c++ AB的

砝码称重

java 3s
你有一架天平和 N 个砝码,这 N 个砝码重量依次是 W1, W2, · · · , WN。
请你计算一共可以称出多少种不同的重量?
注意砝码可以放在天平两边。
输入的第一行包含一个整数 N。
第二行包含 N 个整数:W1, W2, W3, · · · , WN。
输出一个整数代表答案。
【评测用例规模与约定】
对于 50% 的评测用例,1 ≤ N ≤ 15。
对于所有评测用例,1 ≤ N ≤ 100,N 个砝码总重不超过 100000。

动态规划,思路在代码注释中

import java.io.*;

public class Main {
    static final int N = 100010;
    static boolean[][] dp = new boolean[110][2 * N];// 防止栈溢出,定位全局变量吧,dp[i][j]代表在前i个物品中
    // 能否选出组成重量j,每个物品有三种情况,放左边,不放,放右边,(-1 0 1),也就是会组成负数,由总重<=10^5
    // 可以加个大的偏移量,所以 2 * N
    // 左物右码

    public static void main(String[] args) throws IOException {
        StreamTokenizer in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
        PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
        int n, totalW = 0, ans = 0;
        in.nextToken();
        n = (int) in.nval;
        int[] w = new int[n + 1];
        for (int i = 1; i <= n; i++) {
            in.nextToken();
            w[i] = (int) in.nval;
            totalW += w[i];// 总重
        }
        // 在前0个砝码中组成重量0必然为true
        dp[0][N] = true;
        for (int i = 1; i <= n; i++)
            //加偏移量防止负数
            for (int j = -totalW + N; j <= totalW + N; j++) {
                dp[i][j] = dp[i - 1][j]; // 不选的状态转移方程
                if (j - w[i] >= -totalW + N)
                    dp[i][j] |= dp[i - 1][j - w[i]];// 选,放右边
                if (j + w[i] <= totalW + N)
                    dp[i][j] |= dp[i - 1][j + w[i]];// 放左边
            }
        for (int ww = 1 + N; ww <= totalW + N; ww++)
            if (dp[n][ww])
                ans++;
        out.write(ans + "");
        out.flush();
    }
}

G 左孩子右兄弟

image-20220405152140023
时间限制: 3.0s 内存限制: 512.0MB 本题总分:20 分
【问题描述】
对于一棵多叉树,我们可以通过 “左孩子右兄弟” 表示法,将其转化成一棵二叉树。如果我们认为每个结点的子结点是无序的,那么得到的二叉树可能不唯一。换句话说,每个结点可以选任意子结点作为左孩子,并按任意顺序连接右兄弟。
给定一棵包含 N 个结点的多叉树,结点从 1 至 N 编号,其中 1 号结点是根,每个结点的父结点的编号比自己的编号小。请你计算其通过 “左孩子右兄弟” 表示法转化成的二叉树,高度最高是多少。注:只有根结点这一个结点的树高度为 0 。

这道题思路比较简单,贪心加dfs,可以发现每次要想取得最大高度,就要把兄弟节点都放在右子树,并且最后一个兄弟节点要放以该节点为根节点的左孩子右兄弟得到的最大高度,遇到孩子节点没有的就结束

卡在这道题很久是因为,上面的非官方平台限时1s,并且发现上面运行的时间(2000ms)远大于官方的时间(156ms),AC10%还以为是哪错了,所以建议提交用官方的

import java.io.*;
import java.util.*;

public class Main {
    static TreeNode1[] tree = new TreeNode1[100010];

    public static void main(String[] args) throws IOException {
        int n = 0, m;
        StreamTokenizer in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
        PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
        in.nextToken();
        n = (int) in.nval;
        m = n;
        tree[1] = new TreeNode1();
        for (int i = 2; i <= n; i++) {
            in.nextToken();
            tree[i] = new TreeNode1();
            tree[(int) in.nval].son.add(i);
        }
        out.write(getMaxHeight(1) + "");
        out.flush();
    }

    public static int getMaxHeight(int root) {
        int childSize = tree[root].son.size(), res = childSize;
        for (int i = 0; i < childSize; i++)
            res = Math.max(res, childSize + getMaxHeight(tree[root].son.get(i)));
        return res;
    }
}

class TreeNode1 {
    List<Integer> son = new ArrayList<>();
}

加一个B组的杨辉三角吧,这道题挺难的

image-20220406010808270

这道题讲解看b站y总的视频讲解就行了

IMG_0468

import java.util.Scanner;

//数据<= 10^9
public class Main {
	static int n;

	public static void main(String[] args) {
		Scanner in = new Scanner(System.in);
		n = in.nextInt();
		// C34 17 > 10^9 C32 16 < 10 ^9, 所以数据一定在前16正斜行,倒序一行一行检查,不能正序,因为第二行1 2 3 4 5.。。。必然能查到,但不是第一个,第一个必然是逆序先检查到的
		for (int i = 16; i >= 0; i--)
			if (check(i))
				break;
	}

	// 求排列组合Cab
	public static long C(int a, int b) {
        if (a < b)
			return 0;
        //符合C0 0 为1
		double ans = 1;// Ca0为1
		for (int i = a, j = 1; j <= b; i--, j++) {
			ans *= (double) i / j;//这样算不用double会算错
			// 如果值已经比n大了,没必要再求了,可以作为二分的条件了,防止数值过大,溢出
			if (ans > n)
				break;
		}
		return (long) (ans + 0.5);//四舍五入
	}

	// 检查正斜着第x行(从0开始)是否有n
	public static boolean check(int x) {
		int l = 2 * x, r = n; // Crx r的边界
		// 如果l大于r就不行,直接return,没有这个的话输入1输出3,跑到了右边C1 1
		if (l > r)
			return false;
		while (l < r) {
			int mid = (l + r) >> 1;
			long temp = C(mid, x);
			if (temp > n)
				r = mid;
			else if (temp < n)
				l = mid + 1;
			else {
				System.out.println((long)mid * (mid + 1) / 2 + x + 1);
				return true;
			}
		}
		if (C(r, x) == n) {
			long a = ((long) r * (r + 1) / 2 + x + 1);// 不转long会溢出
			System.out.println(a);
			return true;
		}
		return false;
	}
}

小结:组合数公式

leetcode119. 杨辉三角 II
//自己想的
class Solution {
    public List<Integer> getRow(int rowIndex) {
        List<Integer> ans = new ArrayList<>();
//        int k = rowIndex / 2 + 1;
        for (int i = 0; i < rowIndex + 1; i++) {
            ans.add(C(rowIndex, i));
        }
        return ans;
    }

    static public int C(int x, int y) {
        double res = 1;
        for (int i = x, u = 1; u <= y; i--, u++) {
            res *= (double) i / u;
        }
        return (int) (res + 0.5);
    }
}

image-20220406211359744

//官方
class Solution {
    public List<Integer> getRow(int rowIndex) {
        List<Integer> row = new ArrayList<Integer>();
        row.add(1);
        for (int i = 1; i <= rowIndex; ++i) {
            row.add((int) ((long) row.get(i - 1) * (rowIndex - i + 1) / i));
        }
        return row;
    }
}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值