• 蓝桥杯 Java B组 省赛真题详解及小结汇总【2013年(第4届)~2020年(第11届)】

  • 注意:部分代码及程序 源自 蓝桥杯 官网视频(历年真题解析) 郑未老师。
  1. 2013年 第04届 蓝桥杯 Java B组 省赛真题详解及小结
  2. 2014年 第05届 蓝桥杯 Java B组 省赛真题详解及小结
  3. 2015年 第06届 蓝桥杯 Java B组 省赛真题详解及小结
  4. 2016年 第07届 蓝桥杯 Java B组 省赛真题详解及小结
  5. 2017年 第08届 蓝桥杯 Java B组 省赛真题详解及小结
  6. 2018年 第09届 蓝桥杯 Java B组 省赛真题详解及小结
  7. 2019年 第10届 蓝桥杯 Java B组 省赛真题详解及小结
  8. 2020年 第11届 蓝桥杯 第1次模拟赛真题详解及小结(校内模拟)【Java版】
  9. 2020年 第11届 蓝桥杯 第2次模拟赛真题详解及小结【Java版】
  10. 2020年 第11届 蓝桥杯 C/C++ B组 省赛真题详解及小结【第1场省赛 2020.7.5】【Java版】
  11. 2020年 第11届 蓝桥杯 Java B组 省赛真题详解及小结【第1场省赛 2020.7.5】
  12. 2020年 第11届 蓝桥杯 Java C组 省赛真题详解及小结【第1场省赛 2020.7.5】

  • 第11届 蓝桥杯-第1、2次模拟(软件类)真题-(2020年3月、4月)-官方讲解视频

目   录

一、12.5MB

二、最多边数

三、单词重排

解法一:全排列 + Set去重

解法二:dfs

四、括号序列

解法一:手工计算

解法二:递归

五、反倍数

六、凯撒加密

解法一:字符串.toCharArray()

解法二:add()函数

七、螺旋

八、摆动序列

解法1:dfs

优化1:修改递归式

优化2:改为递推

九、通电

十、植树

解法一

优化

小结


   模拟赛(第二轮)真题解析-官方讲解视频

一、12.5MB

【问题描述】

在计算机存储中,12.5MB是多少字节?

【答案提交】

这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。

【答案】:13107200

【解析】:1K=1024字节   ∴ 12.5*1024*1024

   

  • 1Byte(字节) = 8bit(位)   1K = 1024Byte(字节)   
  • 字节也叫baiByte,是计算机数据的基本存储单位,在电脑du里一个中zhi文字占两个字节。

二、最多边数

【问题描述】

一个包含有2019个结点的有向图,最多包含多少条边?(不允许有重边)

【答案提交】

这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。

【答案】:4074342

【解析】:任意两点组成边,边有向,一来一回算两条边。       所以是n中选2的组合数乘以2级n*(n-1)

最大边数 =  * 2 ,∴ 最大边数 = ( 2019 * 2018 / 2 ) * 2 = 4074342

      计算器 计算 即可!

三、单词重排

【问题描述】

将LANQIAO中的字母重新排列,可以得到不同的单词,如LANQIAO、AAILNOQ等,注意这7个字母都要被用上,单词不一定有具体的英文意义。

请问,总共能排列出多少个不同的单词。

【答案提交】

这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。

【答案】:2520

【解析】:全排列 + 去重 + 计数

解法一:全排列 + Set去重

package simulationMatch_11_2020_2;import java.util.HashSet;
import java.util.Set;public class _03_单词重排 {static Set<String> set = new HashSet<String>(); // 不包含重复元素public static void main(String[] args) {char[] str = { 'L', 'A', 'N', 'Q', 'I', 'A', 'O' };f(str, 0);for(String x:set) {System.out.println(x);}System.out.println(set.size());}public static void f(char[] charArray, int k) {if (k == charArray.length) {String s = new String(charArray);set.add(s);}for (int i = k; i < charArray.length; i++) {char temp = charArray[i];charArray[i] = charArray[k];charArray[k] = temp;f(charArray, k + 1);temp = charArray[i];charArray[i] = charArray[k];charArray[k] = temp;}}}

解法二:dfs

package simulationMatch_11_2020_2;import java.util.HashSet;
import java.util.Set;/*** @Author zhengwei* @Date 2020/5/17 5:32 PM* @Version 1.0*/
public class _03_单词重排2 {private static char[] a = "LANQIAO".toCharArray();private static Set<String> ans = new HashSet<>();private static char[] tmp = new char[7];private static boolean[] vis = new boolean[7];public static void main(String[] args) {dfs(0);System.out.println(ans.size());}private static void dfs(int k) {if (k == 7) {ans.add(new String(tmp));return;}for (int i = 0; i < 7; i++) {if (!vis[i]) { // 没有被选入tmp[k] = a[i];vis[i] = true;dfs(k + 1); // 确定下一位vis[i] = false; // 回溯}}}
}

四、括号序列

【问题描述】

由1对括号,可以组成一种合法括号序列:()。

由2对括号,可以组成两种合法括号序列:()()、(())。

由4对括号组成的合法括号序列一共有多少种?

【答案提交】

这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。

【答案】:14

解法一:手工计算

统计“4对括号组成的合法括号序列”,可以分为4种情况:

  1. 括号不嵌套:()()()()
  2. 1个括号嵌套1个括号:(())()()、()(())()、()()(())、(())(())
  3. 1个括号嵌套2个括号:((()))()、()((()))、(()())()、()(()())
  4. 1个括号嵌套3个括号:(((())))、(()()())、(()(()))、((())())、((()()))

∴ 1+4+4+5

解法二:递归

典型的递归结构,每个位置有两种选择,要么左括号,要么右括号。

选左括号的条件:可选数>0 且 变化后存在的左括号数量始终大于等于已存在的右括号数量。

选右括号类似。

package simulationMatch_11_2020_2;/*** @Author zhengwei* @Date 2020/5/17 5:42 PM* @Version 1.0*/public class _04_括号序列 {public static void main(String[] args) {System.out.println(solve(4, 4, 4));}/**** @param n 对的数量* @param l 剩余左括号数量* @param r 剩余右括号的数量* @return*/private static int solve(int n, int l, int r) {if (l == 0 && r == 0)return 1;int ans = 0;// # 要么选左括号,要么选右括号// # 选左括号的条件:l>0 且 变化后存在的左括号数量始终大于等于已存在的右括号数量if (l > 0 && n - (l - 1) >= n - r)ans += solve(n, l - 1, r);if (r > 0 && n - l >= n - (r - 1))ans += solve(n, l, r - 1);return ans;}}

五、反倍数

【问题描述】

给定三个整数 a, b, c,如果一个整数既不是 a 的整数倍 也不是 b 的整数倍 还不是 c 的整数倍,则这个数称为反倍数。

请问在 1 至 n 中有多少个反倍数。

【输入格式】

输入的第一行包含一个整数 n。

第二行包含三个整数 a, b, c,相邻两个数之间用一个空格分隔。

【输出格式】

输出一行包含一个整数,表示答案。

【样例输入】

30

2 3 6

【样例输出】

10

【样例说明】

以下这些数满足要求:1, 5, 7, 11, 13, 17, 19, 23, 25, 29。

【评测用例规模与约定】

对于 40% 的评测用例,1 <= n <= 10000。

对于 80% 的评测用例,1 <= n <= 100000。

对于所有评测用例,1 <= n <= 1000000,1 <= a <= n,1 <= b <= n,1 <= c <= n。

【解析】:迭代 + check

package simulationMatch_11_2020_2;import java.util.Scanner;public class _05_反倍数 {public static void main(String[] args) {Scanner sc = new Scanner(System.in);int n = sc.nextInt();int a = sc.nextInt();int b = sc.nextInt();int c = sc.nextInt();int answer = 0;for (int i = 1; i <= n; i++) { // i < n + 1if (i % a != 0 && i % b != 0 && i % c != 0) {answer++;}}System.out.println(answer);}}

六、凯撒加密

【问题描述】

给定一个单词,请使用凯撒密码将这个单词加密。

凯撒密码是一种替换加密的技术,单词中的所有字母都在字母表上向后偏移3位后被替换成密文。即a变为d,b变为e,...,w变为z,x变为a,y变为b,z变为c。

例如,lanqiao会变成odqtldr。

【输入格式】

输入一行,包含一个单词,单词中只包含小写英文字母。

【输出格式】

输出一行,表示加密后的密文。

【样例输入】

lanqiao

【样例输出】

odqtldr

【评测用例规模与约定】

对于所有评测用例,单词中的字母个数不超过100。

【解析】: 遍历 + 转换,再拼成字符串

解法一:字符串.toCharArray()

package simulationMatch_11_2020_2;import java.util.Scanner;public class _06_凯撒加密 {public static void main(String[] args) {Scanner sc = new Scanner(System.in);String str = sc.next();char[] temp = str.toCharArray();for (int i = 0; i < str.length(); i++) {if (temp[i] == 'x') {temp[i] = 'a';} else if (temp[i] == 'y') {temp[i] = 'b';} else if (temp[i] == 'z') {temp[i] = 'c';} else {temp[i] += 3;}}System.out.println(new String(temp)); // 字符数组转字符串
//      for (char x : temp) {
//          System.out.print(x + "");
//      }}}

解法二:add()函数

package simulationMatch_11_2020_2;import java.util.Scanner;/*** @Author zhengwei* @Date 2020/5/17 5:51 PM* @Version 1.0*/
public class _06_凯撒加密2 {private static char add(char letter) {if (letter < 'x')return (char) (letter + 3);else if (letter == 'x')return 'a';else if (letter == 'y')return 'b';elsereturn 'c';}public static void main(String[] args) {Scanner sc = new Scanner(System.in);String s = sc.next();char[] ans = new char[s.length()];for (int i = 0; i < s.length(); i++) {ans[i] = add(s.charAt(i));}System.out.println(new String(ans));}
}

七、螺旋

【问题描述】

对于一个 n 行 m 列的表格,我们可以使用螺旋的方式给表格依次填上正整数,我们称填好的表格为一个螺旋矩阵。

例如,一个 4 行 5 列的螺旋矩阵如下:

1 2 3 4 5

14 15 16 17 6

13 20 19 18 7

12 11 10 9 8

【输入格式】

输入的第一行包含两个整数 n, m,分别表示螺旋矩阵的行数和列数。

第二行包含两个整数 r, c,表示要求的行号和列号。

【输出格式】

输出一个整数,表示螺旋矩阵中第 r 行第 c 列的元素的值。

【样例输入】

4 5

2 2

【样例输出】

15

【评测用例规模与约定】

对于 30% 的评测用例,2 <= n, m <= 20。

对于 70% 的评测用例,2 <= n, m <= 100。

对于所有评测用例,2 <= n, m <= 1000,1 <= r <= n,1 <= c <= m。

【解析】:按题意生成网格,再取单元格中的数据

package simulationMatch_11_2020_2;import java.util.Scanner;public class _07_螺旋 {public static void main(String[] args) {Scanner sc = new Scanner(System.in);int n = sc.nextInt();int m = sc.nextInt();int r = sc.nextInt();int c = sc.nextInt();int[][] grid = new int[n][m];int num = 1;int up = 0, down = n, left = 0, right = m;while (true) {for (int col = left; col < right; col++) {grid[up][col] = num;num++;}up++;if (up == down)break;for (int row = up; row < down; row++) {grid[row][right - 1] = num;num++;}right--;if (left == right)break;for (int col = right - 1; col >= left; col--) {grid[down - 1][col] = num;num++;}down -= 1;if (up == down)break;for (int row = down - 1; row >= up; row--) {grid[row][left] = num;num++;}left++;if (left == right)break;}System.out.println(grid[r - 1][c - 1]);}}
/**
int[][] array = new int[n][m];
int x = 1; // 递增数字
int index = 0; // 循环标志
int count = n / 2 + n % 2; // 循环结束判断标识
while (count > 0) {for (int i = index; i < m; i++) { // 第1行array[0][i] = x++;}for (int i = 1; i < n; i++) { // 第m列(最后一列)array[i][m - 1] = x++;}for (int i = m; i > 0; i--) { // 第n行(最后一行)array[n - 1][i - 1] = (x++ - 1);}for (int i = n - 1; i > 0; i--) { // 第1列array[i][0] = (x++ - 2);}index++;for (int i = 0; i < n; i++) {for (int j = 0; j < m; j++) {System.out.print(array[i][j] + " ");}System.out.println();}System.out.println("------");count--;
}
*/

八、摆动序列

【问题描述】

如果一个序列的奇数项都比前一项大,偶数项都比前一项小,则称为一个摆动序列。即 a[2i]<a[2i-1], a[2i+1]<a[2i]。

小明想知道,长度为 m,每个数都是 1 到 n 之间的正整数的摆动序列一共有多少个。

【输入格式】

输入一行包含两个整数 m,n。

【输出格式】

输出一个整数,表示答案。答案可能很大,请输出答案除以10000的余数。

【样例输入】

3 4

【样例输出】

14

【样例说明】

以下是符合要求的摆动序列:

2 1 2

2 1 3

2 1 4

3 1 2

3 1 3

3 1 4

3 2 3

3 2 4

4 1 2

4 1 3

4 1 4

4 2 3

4 2 4

4 3 4

【评测用例规模与约定】

对于 20% 的评测用例,1 <= n, m <= 5;

对于 50% 的评测用例,1 <= n, m <= 10;

对于 80% 的评测用例,1 <= n, m <= 100;

对于所有评测用例,1 <= n, m <= 1000。

解法1:dfs

多数人都能想到:第1位,可选为[2,n]
选定第1位(last1),开始选第2位,可选为[1,last1-1],对所有last1结果求和
选定第2位(last2),开始选第3位,可选为[last2+1,n],对所有last2结果求和
……
选定奇数位(last),开始选下一个偶数位,可选为[1,last-1],对所有last结果求和
选定偶数位(last),开始选下一个奇数位,可选为[last+1,n],对所有last结果求和
……

递归式为:
dfs(last, k) = Σdfs(i, k + 1) | k为奇数,i from 1 to last-1
dfs(last, k) = Σdfs(i, k + 1) | k为偶数,i from last+1 to n

而递归起点(选定第一位,可选是2 to n)也是一个循环:

for i in range(2, n + 1):ans = (ans + dfs(i, 1)) % MOD

这里面有大量重复子问题,所以可以记忆型递归;但只能过80%的数据,因为复杂度是O(N³)

package simulationMatch_11_2020_2;import java.util.Scanner;public class _08_摆动序列 {private static final int MOD = 10000;private static int[][] mem = new int[1000][1000];private static int n, m;// 第k个数确定为last时,序列总数是多少// param last:确定的最后一个数// param k:last是第k个private static int dfs(int last, int k) {if (k == m)return 1;if (mem[last][k] != 0)return mem[last][k];// k是奇数,k+1是偶数,偶数位比前一个小if ((k & 1) == 1)for (int i = 1; i < last; i++)mem[last][k] = (mem[last][k] + dfs(i, k + 1)) % MOD;elsefor (int i = last + 1; i < n + 1; i++)mem[last][k] = (mem[last][k] + dfs(i, k + 1)) % MOD;return mem[last][k];}public static void main(String[] args) {Scanner sc = new Scanner(System.in);m = sc.nextInt();n = sc.nextInt();int ans = 0;// 第一位可以选2到nfor (int i = 2; i < n + 1; i++)ans = (ans + dfs(i, 1)) % MOD;System.out.println(ans);}}
/**
int answer = 0;
int array[] = new int[m];
for (int i = 0; i < m; i++) {f(array); // 对数组元素进行赋值if(m % 2 == 1) { // m是奇数for (int k = 2; k < m; k += 2) {if (array[k - 1] < array[k] && array[k] < array[k + 1]) {answer++;}}} else { // m是偶数for (int k = 0; k < m; k += 2) {if (array[k - 1] < array[k] && array[k] < array[k + 1]) {answer++;}}}
}
System.out.println(answer % 10000);
*/

优化1:修改递归式

这种在递归中加总的递归形态,往往可以通过优化递归式来改进,将递归式变成有汇总 or 集合的意义,就可以减少一层循环,从而把复杂度变为O(N²)

可以从递归起点的那个循环考虑,我们既然要加总第1位选2到n的这若干种情况的结果,为什么不用dfs(2,1)直接表示第一位选[2,n]这所有情况的和呢?这就是集合的概念了。

更通用地:
k为奇数时,dfs(x,k)表示第k位选[x,n]这若干种情况的种数和;
k为偶数时,dfs(x,k)表示第k位选[1,x]这若干种情况的种数和;
但是怎么拆呢?技巧是拆成一个元素+一个(少了该元素的)小集合,小集合动一个变量(往往代表规模),那拆出来的元素往往可以转换成另外一个集合。

就本题来说,k为奇数时,dfs(x,k)可以这样拆:

k为偶数时,dfs(x,k)可以这样拆:

 
那么可得递归式:

dfs(x, k) = dfs(x+1, k) + dfs(x-1,k+1) | k为奇数
dfs(x, k) = dfs(x-1, k) + dfs(x+1,k+1) | k为偶数

递归起点为:ans = dfs(2, 1)

package simulationMatch_11_2020_2;import java.util.Scanner;public class _08_摆动序列2_优化1 {private static final int MOD = 10000;private static int[][] mem = new int[1000][1000];private static int n, m;// 第k个数确定为last时,序列总数是多少// param last:确定的最后一个数// param k:last是第k个private static int dfs(int last, int k) {if (last < 1 || last > n)return 0;if (k == m) {// 奇数,return 大于等于last的个数;偶数,小于等于last的个数if ((k & 1) == 1) {mem[last][k] = n - last + 1;} else {mem[last][k] = last;}return mem[last][k];}if (mem[last][k] != 0)return mem[last][k];if ((k & 1) == 1)// 注意看这里的拆解:当前函数的含义是第k位选last~n的序列数总和,切成两块// 1:第k位选(last+1)到n的序列数总和,函数含义不变,第一个参数变为last+1==》dfs(last + 1, k)// 2:第k位固定为last,那么第k+1位的选择是从1到last-1(因k+1是偶数)==》dfs(last - 1, k + 1)mem[last][k] = (dfs(last + 1, k) + dfs(last - 1, k + 1)) % MOD;else // 偶数mem[last][k] = (dfs(last - 1, k) + dfs(last + 1, k + 1)) % MOD;return mem[last][k];}public static void main(String[] args) {Scanner sc = new Scanner(System.in);m = sc.nextInt();n = sc.nextInt();System.out.println(dfs(2, 1));}}

优化2:改为递推

为什么还要优化呢?因为这样递归层次太深,会超出栈空间限制。

递推是递归的逆过程,因此我们观察上述递归函数的出口,就知道怎么初始化dp数组,再按照与递归相逆的顺序逐步生成递推数组。

递归出口:

if (k == m) {// 奇数,return 大于等于last的个数;偶数,小于等于last的个数if ((k & 1) == 1) {mem[last][k] = n - last + 1;} else {mem[last][k] = last;}return mem[last][k];
}

转变为数组初始化:

//初始化最后一列
for (int x = 1; x < n + 1; x++) {if ((m & 1) == 1)dp[x][m] = n - x + 1;elsedp[x][m] = x;
}

递归中,列数k是逐渐增大,直至最后一列,那么递推中,应该从最后一列反推到第一列。

package simulationMatch_11_2020_2;import java.util.Scanner;public class _08_摆动序列2_优化2 {private static final int MOD = 10000;private static int[][] dp;private static int n, m;public static void main(String[] args) {Scanner sc = new Scanner(System.in);m = sc.nextInt();n = sc.nextInt();dp = new int[n + 1][m + 1];// 初始化最后一列for (int x = 1; x < n + 1; x++) {if ((m & 1) == 1)dp[x][m] = n - x + 1;elsedp[x][m] = x;}for (int k = m - 1; k > 0; k--) {// # 奇数,x从大到小遍历if ((k & 1) == 1)for (int x = n; x > 0; x--)dp[x][k] = ((x + 1 <= n ? dp[x + 1][k] : 0) + dp[x - 1][k + 1]) % MOD;// # 偶数,x从小到大遍历elsefor (int x = 1; x < n + 1; x++)dp[x][k] = (dp[x - 1][k] + (x + 1 <= n ? dp[x + 1][k + 1] : 0)) % MOD;}System.out.println(dp[2][1]);}}

九、通电

【问题描述】

2015年,全中国实现了户户通电。作为一名电力建设者,小明正在帮助一带一路上的国家通电。

这一次,小明要帮助 n 个村庄通电,其中 1 号村庄正好可以建立一个发电站,所发的电足够所有村庄使用。

现在,这 n 个村庄之间都没有电线相连,小明主要要做的是架设电线连接这些村庄,使得所有村庄都直接或间接的与发电站相通。

小明测量了所有村庄的位置(坐标)和高度,如果要连接两个村庄,小明需要花费两个村庄之间的坐标距离加上高度差的平方,形式化描述为坐标为 (x_1, y_1) 高度为 h_1 的村庄与坐标为 (x_2, y_2) 高度为 h_2 的村庄之间连接的费用为

sqrt((x_1-x_2)*(x_1-x_2)+(y_1-y_2)*(y_1-y_2))+(h_1-h_2)*(h_1-h_2)。

在上式中 sqrt 表示取括号内的平方根。请注意括号的位置,高度的计算方式与横纵坐标的计算方式不同。

由于经费有限,请帮助小明计算他至少要花费多少费用才能使这 n 个村庄都通电。

【输入格式】

输入的第一行包含一个整数 n ,表示村庄的数量。

接下来 n 行,每个三个整数 x, y, h,分别表示一个村庄的横、纵坐标和高度,其中第一个村庄可以建立发电站。

【输出格式】

输出一行,包含一个实数,四舍五入保留 2 位小数,表示答案。

【样例输入】

4

1 1 3

9 9 7

8 8 6

4 5 4

【样例输出】

17.41

【评测用例规模与约定】

对于 30% 的评测用例,1 <= n <= 10;

对于 60% 的评测用例,1 <= n <= 100;

对于所有评测用例,1 <= n <= 1000,0 <= x, y, h <= 10000。

【解析】:最小生成树

可以说是最小生成树的裸题了——连通==树,代价最小==最小生成
用Kruskal算法一气呵成。
不过要进行一些处理:
每个村庄看做是一个顶点,编号存储
两两组成边,用费用做边的权重

做好数据处理,然后就是排序,从小到大把边添加到最小生成树的边集(也不用真正添加,符合的边把代价累加就行,不符合的边忽略)

符合不符合,当然要用并查集了。

并查集 一定要掌握。

package simulationMatch_11_2020_2;import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Scanner;public class _09_通电 {public static void main(String[] args) {Scanner sc = new Scanner(System.in);int n = sc.nextInt();// 接下来 n 行,每个三个整数 x, y, hint[][] data = new int[n + 1][3];for (int i = 1; i <= n; i++) {data[i][0] = sc.nextInt();data[i][1] = sc.nextInt();data[i][2] = sc.nextInt();}// # 将原始数据处理成边集,每两个点一条边,计算代价List<Edge> edges = new ArrayList<>(n * n);for (int i = 1; i <= n - 1; i++) {for (int j = i + 1; j <= n; j++) {edges.add(new Edge(i, j, cost(data[i], data[j])));}}// 对边集排序Collections.sort(edges);// 初始化并查集工具UF uf = new UF(n);int edge_cnt = 0;double ans = 0;for (Edge e : edges) {if (uf.find(e.x) != uf.find(e.y)) {uf.union(e.x, e.y);edge_cnt++;ans += e.cost;if (edge_cnt == n - 1)break;}}System.out.printf("%.2f", ans);}/*** 封装并查集操作*/private static class UF {int n;int[] parent;public UF(int n) {this.n = n;parent = new int[n + 1];for (int i = 1; i <= n; i++) {parent[i] = i;}}int find(int x) {if (parent[x] == x)return x;HashSet<Integer> path = new HashSet<>();while (parent[x] != x) {path.add(x);x = parent[x];}for (Integer xx : path) {parent[xx] = x;}return x;}void union(int a, int b) {parent[find(b)] = find(a);}}/*** 计算a,b两个村庄的建设代价* * @param a* @param b* @return*/private static double cost(int[] a, int[] b) {return Math.sqrt((a[0] - b[0]) * (a[0] - b[0]) + (a[1] - b[1]) * (a[1] - b[1])) + (a[2] - b[2]) * (a[2] - b[2]);}/*** 注意实现Comparable接口*/private static class Edge implements Comparable<Edge> {int x;int y;double cost;public Edge(int x, int y, double cost) {this.x = x;this.y = y;this.cost = cost;}@Overridepublic int compareTo(Edge o) {return this.cost < o.cost ? -1 : (this.cost == o.cost ? 0 : 1);}}}

十、植树

【问题描述】

小明和朋友们一起去郊外植树,他们带了一些在自己实验室精心研究出的小树苗。

小明和朋友们一共有 n 个人,他们经过精心挑选,在一块空地上每个人挑选了一个适合植树的位置,总共 n 个。他们准备把自己带的树苗都植下去。

然而,他们遇到了一个困难:有的树苗比较大,而有的位置挨太近,导致两棵树植下去后会撞在一起。

他们将树看成一个圆,圆心在他们找的位置上。如果两棵树对应的圆相交,这两棵树就不适合同时植下(相切不受影响),称为两棵树冲突。

小明和朋友们决定先合计合计,只将其中的一部分树植下去,保证没有互相冲突的树。他们同时希望这些树所能覆盖的面积和(圆面积和)最大。

【输入格式】

输入的第一行包含一个整数 n ,表示人数,即准备植树的位置数。

接下来 n 行,每行三个整数 x, y, r,表示一棵树在空地上的横、纵坐标和半径。

【输出格式】

输出一行包含一个整数,表示在不冲突下可以植树的面积和。由于每棵树的面积都是圆周率的整数倍,请输出答案除以圆周率后的值(应当是一个整数)。

【样例输入】

6

1 1 2

1 4 2

1 7 2

4 1 2

4 4 2

4 7 2

【样例输出】

12

【评测用例规模与约定】

对于 30% 的评测用例,1 <= n <= 10;

对于 60% 的评测用例,1 <= n <= 20;

对于所有评测用例,1 <= n <= 30,0 <= x, y <= 1000,1 <= r <= 1000。

解法一

【解析】:

每个圆可以选也可以不选,但不知道哪种决策结果最大,只能先考虑暴力搜索每种情况,总可选数为2的n次方的深度优先搜索。

某一个圆在准备选入的时候,可以判断是否与之前已入选的圆冲突,如果冲突了,这条分支就可以不继续了,这可以视为剪枝;但是判断是否冲突的check函数要遍历已入选的圆,复杂度依然高;

【代码1】在n等于30的时候会吃不消。

package simulationMatch_11_2020_2;import java.util.Scanner;public class _10_植树 {static Scanner sc = new Scanner(System.in);static int n;static int ans = 0;static Tree[] trees;static int[][] adjaTable;public static void main(String[] args) {n = sc.nextInt();initTrees();initAdjaTable();dfs(0, 0);System.out.println(ans);}/*** 初始化邻接矩阵*/private static void initAdjaTable() {adjaTable = new int[n][n];for (int i = 0; i < n - 1; i++) {for (int j = i + 1; j < n; j++) {if (trees[i].intersected(trees[j])) {adjaTable[i][j] = 1;adjaTable[j][i] = 1;}}}}/*** 初始化每棵树并加入数组*/private static void initTrees() {trees = new Tree[n];for (int i = 0; i < n; i++) {trees[i] = new Tree(sc.nextInt(), sc.nextInt(), sc.nextInt());}}private static void dfs(int sum, int index) {// 边界if (index == n) {ans = Math.max(ans, sum);return;}// 2.选这棵树(是有条件的)if (ok(index)) {trees[index].selected = true;int r = trees[index].r;dfs(sum + r * r, index + 1);trees[index].selected = false; // 回溯}// 1.不选当前这棵树trees[index].selected = false;dfs(sum, index + 1);}private static boolean ok(int index) {for (int i = 0; i < index; i++) {// i被选入,且i与当前准备入选的index相交,则index代表的树不能入选if (trees[i].selected && adjaTable[i][index] == 1)return false;}return true;}private static class Tree {int x, y, r;boolean selected; // 是否入选public Tree(int x, int y, int r) {this.x = x;this.y = y;this.r = r;}/** 与另一颗树是否相交 */public boolean intersected(Tree other) {int dis = (this.x - other.x) * (this.x - other.x) + (this.y - other.y) * (this.y - other.y);return dis < (this.r + other.r) * (this.r + other.r);}}}

优化

优化的关键点在于用类似贪心的办法(但不是贪心):将圆按半径从大到小排序,这样优先考虑半径大的圆的选与不选问题;另外把“选”这个分支放在“不选”这个分支前面执行,这样我们相信会尽早地遇到最优解。

基于这个假设,在递归之前我们可以以O(N)的复杂度存储所有圆的“半径的平方”的后缀和,计为数组s;在递归函数dfs中,参数sum代表index之前的选择策略所得到的sum,s[index]代表包括index索引及之后续所有圆的半径的平方和,如果sum+s[index]小于等于已经求得的ans,那就不必进行任何后续的选择试探了,可立即退出递归。

实测,【代码2】在n=30时能秒出结果。

package simulationMatch_11_2020_2;import java.time.Duration;
import java.time.Instant;
import java.util.Arrays;
import java.util.Scanner;public class _10_植树2 {static Scanner sc = new Scanner(System.in);static int n;static int ans = 0;static Tree[] trees;static int[][] adjaTable;/** 半径的平方后缀和 */static int[] suffix;public static void main(String[] args) {// Instant now = Instant.now();n = sc.nextInt();initTrees();initSuffix();initAdjaTable();dfs(0, 0);System.out.println(ans);// System.err.println("Duration:" + Duration.between(now,// Instant.now()).toMillis());}private static void initSuffix() {suffix = new int[n];suffix[n - 1] = trees[n - 1].pow_r;for (int i = n - 2; i >= 0; i--) {// 后缀和加当前项的平方suffix[i] = suffix[i + 1] + trees[i].pow_r;}}/*** 初始化邻接矩阵*/private static void initAdjaTable() {adjaTable = new int[n][n];for (int i = 0; i < n - 1; i++) {for (int j = i + 1; j < n; j++) {if (trees[i].intersected(trees[j])) {adjaTable[i][j] = 1;adjaTable[j][i] = 1;}}}}/*** 初始化每棵树并加入数组*/private static void initTrees() {trees = new Tree[n];for (int i = 0; i < n; i++) {trees[i] = new Tree(sc.nextInt(), sc.nextInt(), sc.nextInt());}// !!!!排序Arrays.sort(trees);}private static void dfs(int sum, int index) {// 边界if (index == n) {ans = Math.max(ans, sum);return;}// !!!如果index之前的sum加上自index开始的半径的平方和小于ans,则没必要继续if (sum + suffix[index] <= ans)return;// 2.选这棵树(是有条件的)if (ok(index)) {trees[index].selected = true;dfs(sum + trees[index].pow_r, index + 1);trees[index].selected = false; // 回溯}// 1.不选当前这棵树trees[index].selected = false;dfs(sum, index + 1);}private static boolean ok(int index) {for (int i = 0; i < index; i++) {// i被选入,且i与当前准备入选的index相交,则index代表的树不能入选if (trees[i].selected && adjaTable[i][index] == 1)return false;}return true;}private static class Tree implements Comparable<Tree> {int x, y, r, pow_r;boolean selected; // 是否入选public Tree(int x, int y, int r) {this.x = x;this.y = y;this.r = r;pow_r = r * r;}/** 与另一颗树是否相交 */public boolean intersected(Tree other) {int dis = (this.x - other.x) * (this.x - other.x) + (this.y - other.y) * (this.y - other.y);return dis < (this.r + other.r) * (this.r + other.r);}@Overridepublic int compareTo(Tree o) {return this.r - o.r;}}
}

小结

Python组 第9题 练功

扩展多少步达到目标,一般用bfs求解。

bfs、dfs:走过的路 不再走。

BufferedWriter:对输出进行优化。缓冲式输出,内存积累到一定量,才向控制台进行输出。

2020年 第11届 蓝桥杯 第2次模拟赛真题详解及小结【Java版】相关推荐

  1. 2021年 第12届 蓝桥杯 第4次模拟赛真题详解及小结【Java版】

    蓝桥杯 Java B组 省赛决赛 真题详解及小结汇总[2013年(第4届)~2021年(第12届)] 第11届 蓝桥杯-第1.2次模拟(软件类)真题-(2020年3月.4月)-官方讲解视频 说明:大部 ...

  2. 2021年 第12届 蓝桥杯 第3次模拟赛真题详解及小结【Java版】

    蓝桥杯 Java B组 省赛决赛 真题详解及小结汇总[2013年(第4届)~2021年(第12届)] 第11届 蓝桥杯-第1.2次模拟(软件类)真题-(2020年3月.4月)-官方讲解视频 说明:大部 ...

  3. 【第十三届蓝桥杯C++ B组省赛编程题详解】

    第十三届蓝桥杯C++ B组省赛编程题详解 第一题:刷题统计 题目描述 [Tag:枚举] 小明决定从下周一开始努力刷题准备蓝桥杯竞赛. 他计划周一至周五每天做a道题目,周六和周日每天做b道题目. 请你帮 ...

  4. 2020年 第11届 蓝桥杯 Java B组 省赛真题详解及小结【第1场省赛 2020.7.5】

    蓝桥杯 Java B组 省赛决赛 真题详解及小结汇总[2013年(第4届)~2021年(第12届)] 第11届 蓝桥杯-第1.2次模拟(软件类)真题-(2020年3月.4月)-官方讲解视频 说明:部分 ...

  5. 2020年 第11届 蓝桥杯 Java C组 省赛真题详解及小结【第1场省赛 2020.7.5】

    蓝桥杯 Java B组 省赛真题详解及小结汇总[2013年(第4届)~2020年(第11届)] 注意:部分代码及程序 源自 蓝桥杯 官网视频(历年真题解析) 郑未老师. 2013年 第04届 蓝桥杯 ...

  6. 2020年 第11届 蓝桥杯 C/C++ B组 省赛真题详解及小结【第1场省赛2020.7.5】【Java版】

    蓝桥杯 Java B组 省赛真题详解及小结汇总[2013年(第4届)~2020年(第11届)] 注意:部分代码及程序 源自 蓝桥杯 官网视频(历年真题解析) 郑未老师. 2013年 第04届 蓝桥杯 ...

  7. 2020年 第11届 蓝桥杯 Java B组 省赛真题详解及小结【第2场省赛 2020.10.17】

    蓝桥杯 Java B组 省赛真题详解及小结汇总[2013年(第4届)~2020年(第11届)] 说明:大部分题解思路及程序代码 源自 蓝桥杯 官网视频(Java B组历年真题解析) -- 郑未老师. ...

  8. 2021年 第12届 蓝桥杯 Java B组 省赛真题详解及小结【第1场省赛 2021.04.18】

    蓝桥杯 Java B组 省赛决赛 真题详解及小结汇总[题目下载.2013年(第4届)~2020年(第11届)] CSDN 蓝桥杯 专栏 2013年 第04届 蓝桥杯 Java B组 省赛真题详解及小结 ...

  9. 11届蓝桥杯青少组C++选拔赛真题

    第11届蓝桥杯青少组C++选拔赛真题 一.选择题(单选择,每空30分) 第一题(难度系数1) 一个C++语言的源程序中,有关主函数的说法正确的是(B). A.可以有多个主函数 B.必须有一个主函数 C ...

最新文章

  1. cpu,内存,虚拟内存,硬盘,缓存之间是什么关系??
  2. 预备队员技术验收开始
  3. CC2540开发板学习笔记(六)——AD控制(自带温度计)
  4. 安装batocera-linux教程_batocera游戏系统,一个U盘搞定所有模拟器
  5. java对一个无序列表进行分组
  6. [转]自适应网页设计(Responsive Web Design)
  7. Linux man指令
  8. python对数据进行分类_在Python中对一系列数据进行分类的最佳方法
  9. 上传jar包到nexus私服
  10. JEECG分页条数自定义
  11. 两个list取交集_利用jieba计算两个句子的相似度
  12. h5优秀控件_H5匠人手册:霸屏H5实战解密
  13. ruby+gem常用命令
  14. day09 python之函数进阶
  15. 微信小程序生成体验版的二维码
  16. SPSS实现线性回归
  17. oracle正则表达式匹配字母,oracle正则表达式函数 匹配
  18. “adb”不是内部或外部命令的解决方法
  19. 史上最详细的新浪广告系统技术架构优化历程
  20. 【计算机图形学】零 · 计算机图形系统概述

热门文章

  1. linux 在线帮助,linux教程之在线帮助
  2. fastdfs连接mysql_fastDFS文件上传简单案例
  3. flask mysql 版本_Flask mysql
  4. linux上安装hackrf_在Linux上安装Kubectl
  5. Android蓝牙无法通信,android.bluetooth.BluetoothSocket无法连接
  6. 九十五、二叉树的递归和非递归的遍历算法模板
  7. 八十五、再探希尔排序,桶排序,计数排序和基数排序
  8. ehcache 手动刷新缓存_【第 21 期】一个架构师的缓存修炼之路
  9. SIGIR 2020 | 第四范式提出深度稀疏网络模型,显著提升高维稀疏表数据分类效果...
  10. 选对论文,效率提升50% | 本周值得读