第七届蓝桥杯(2016年)JavaA组省赛真题解析
- 1.煤球数量
- 有一堆煤球,堆成三角堆形,具体如下:
- 第一层放1个
- 第二层放3个
- 第三层放6个
- 第四层放10个
- …
- 如果一共有100层,一共多少个煤球?
/**
* 1.煤球数量
* 有一堆煤球,堆成三角堆形,具体如下:
* 第一层放1个
* 第二层放3个
* 第三层放6个
* 第四层放10个
* ...
* 如果一共有100层,一共多少个煤球?
*/
public class Main {
/**
* 注意:求得是总的煤球数目,不是第100层的煤球数目
* 总的煤球数量等于每一层的煤球数量之和
* 每一层的煤球数量=上一层的煤球数量+当前的层数
*/
static int sum = 0, count = 0;
public static void main(String[] args) {
for(int level=1; level<=100; level++){ //level代表层
count = (count + level) ;//每层的煤球数量
sum += count ; //总的煤球数量
}
System.out.println(sum);
}
}
2.生日蜡烛
- 某君从某年开始,每年都会举办一次生日party,并且每年都要吹熄与年龄相同根树的蜡烛
- 到2016年,他一共吹熄了236根蜡烛,请问他从多少岁开始过生日party的,
- 请给出他开始过生日的年龄数。
/**
* 2.生日蜡烛
* 某君从某年开始,每年都会举办一次生日party,并且每年都要吹熄与年龄相同根树的蜡烛
* 到2016年,他一共吹熄了236根蜡烛,请问他从多少岁开始过生日party的,
* 请给出他开始过生日的年龄数。
*/
public class Main {
static int sum = 0;
public static void main(String[] args) {
for (int j = 1; j <= 100; j++) { //假设从1岁-100岁开始过生日
for (int i = j; sum < 250; i++) {
sum += i;
if(sum == 236){ //找到吹灭蜡烛数量为236的
System.out.println(j); //输出过生日的年龄
break ;
}
}
sum = 0 ;
}
}
}
3.搭积木
- 小明最近喜欢搭数字积木
- 一共有10块积木,每个积木上有一个数字,0-9
- 搭积木规则:
- 每个积木放到其它两个积木的上面,并且一定比下面两个积木数字小。
- 最后搭成4层的金字塔,必须用完所有的积木
- 请你计算这样的搭建方法一共有多少种?
方法1:暴力筛选
import java.util.Set;
import java.util.TreeSet;
/**
* 3.搭积木
* 小明最近喜欢搭数字积木
* 一共有10块积木,每个积木上有一个数字,0-9
* 搭积木规则:
* 每个积木放到其它两个积木的上面,并且一定比下面两个积木数字小。
* 最后搭成4层的金字塔,必须用完所有的积木
* 请你计算这样的搭建方法一共有多少种?
*
*/
public class Main {
static Set<Integer> set ;
static int count = 0 ;
public static void main(String[] args) {
set = new TreeSet<>() ;
for(int a=1; a<=9; a++){
for(int b=1; b<=9; b++){
for(int c=1; c<=9; c++){
for(int d=1; d<=9; d++){
for(int e=1; e<=9; e++){
for(int f=1; f<=9; f++){
for(int g=1; g<=9; g++){
for(int h=1; h<=9; h++){
for(int i=1; i<=9; i++){
if(a < c && a < d && b<d && b < e){
if(c < f && c < g && d < g && d < h && e < h && e < i){
int [] res = {a,b,c,d,e,f,g,h,i} ;
for(int j=0; j<res.length; j++) {
set.add(res[j]);
}
if(set.size() == 9){
count ++ ;
set.clear();
}else{
set.clear();
}
}
}
}
}
}
}
}
}
}
}
}
System.out.println(count);
}
}
方法2:全排列剪枝
/**
* 3.搭积木
* 小明最近喜欢搭数字积木
* 一共有10块积木,每个积木上有一个数字,0-9
* 搭积木规则:
* 每个积木放到其它两个积木的上面,并且一定比下面两个积木数字小。
* 最后搭成4层的金字塔,必须用完所有的积木
* 请你计算这样的搭建方法一共有多少种?
*
*/
public class Main1 {
static int [] arr = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9} ;
static int count = 0;
public static void main(String[] args) {
f(0) ;
System.out.println(count);
}
/**
* 全排列,剪枝法
* @param k
*/
private static void f(int k){
if(k == 10){
count ++ ;
}
for(int i=k; i<10; i++){
swap(i, k) ;
if((k == 1 && arr[1]<arr[0])
|| (k == 2 && arr[2]<arr[0])
|| (k==3 && arr[3]<arr[1])
|| (k==4 && (arr[4]<arr[1] || arr[4]<arr[2]))
|| (k==5 && arr[5]<arr[2])
|| (k==6 && arr[6]<arr[3])
|| (k==7 && (arr[7]<arr[3] || arr[7]<arr[4]))
|| (k==8 && (arr[8]<arr[4] || arr[8]<arr[5]))
|| (k==9 && arr[9]<arr[5])){
swap(i, k) ;
continue ;
}
f(k+1) ;
swap(i,k);
}
}
private static void swap(int i, int k){
int t = arr[i] ;
arr[i] = arr[k] ;
arr[k] = t ;
}
}
4,5代码填空题,略
6.寒假作业
public class Main1 {
static int [] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13} ;
static int count = 0;
public static void main(String[] args) {
f(0) ;
System.out.println(count);
}
/**
* 全排列,剪枝法
* @param k
*/
private static void f(int k){
if(k == 13){ //13个全排列,最后一个不要就可以了
count++;
}
for(int i=k; i<13; i++){
swap(i, k) ;
if(k==2 && arr[0] + arr[1] != arr[2]
|| k==5 && arr[3] - arr[4] != arr[5]
|| k==8 && arr[6] * arr[7] != arr[8]
|| k==11 && ((arr[9] / arr[10] != arr[11]) || (arr[9] % arr[10] != 0))){//取余保证除的都是整除
swap(i, k);
continue;
}
f(k+1) ;
swap(i,k);
}
}
private static void swap(int i, int k){
int t = arr[i] ;
arr[i] = arr[k] ;
arr[k] = t ;
}
}
7.剪邮票
- 有12张连在一起的12生肖的邮票,现在你要从中剪下5张,要求必须是连着的
- 仅仅连一个角不算连,请你计算一共有多少种不同的剪取方法。
/**
* 7.剪邮票
* 有12张连在一起的12生肖的邮票,现在你要从中剪下5张,要求必须是连着的
* 仅仅连一个角不算连,请你计算一共有多少种不同的剪取方法。
*/
/**
* 算法思想:枚举所有5张牌的组合,检查它们是否是一个连通块。
* 12选5的过程可以用全排列去做。
* 也就是用全排列随机的抽取5个格子,然后做连通性检查
* 本题是带有重复元素的全排列,需要将重复的排列规避掉
*/
public class Main {
static int [] a = {0,0,0,0,0,0,0,1,1,1,1,1} ; //每个排列代表着12选5的一个方案
static int ans ;
static boolean [] vis = new boolean [12] ;
static void f(int k, int [] path){
if(k == 12){
if(check(path)){
ans ++ ;
}
}
for(int i=0; i<12; i++){
if(i>0 && a[i] == a[i-1] && !vis[i-1]){ //当前选中的元素与上一个元素相同,且上一个元素未被用到
continue;
}
if(!vis[i]){ //没有被用过的元素可以加入到path
vis[i] = true ; //标记被使用过
path[k] = a[i] ;//将a[i]存到path[k]中
f(k+1, path) ;//递归
vis[i] = false ; //回溯
}
}
}
static boolean check(int [] path){
int [][] g = new int [3][4] ;
//将某个排列映射到二维矩阵上
for(int i=0; i<3; i++){
for(int j=0; j<4; j++){
if(path[4*i+j] == 1){
g[i][j] = 1 ;
}else{
g[i][j] = 0 ;
}
}
}
int cnt = 0 ;
for(int i=0; i<3; i++){
for(int j=0; j<4; j++){
if(g[i][j] == 1){
dfs(g, i, j) ;
cnt ++ ; //一次走完,如果没走完则说明不是联通的
}
}
}
return cnt == 1;
}
private static void dfs(int[][] g, int i, int j) { //向着四个方向搜索
g[i][j] = 0 ;
if(i-1>=0 && g[i-1][j]==1){
dfs(g, i-1, j) ;
}
if(i+1<=2 && g[i+1][j]==1){
dfs(g, i+1, j) ;
}
if(j-1>=0 && g[i][j-1]==1){
dfs(g, i, j-1) ;
}
if(j+1<=3 && g[i][j+1]==1){
dfs(g, i, j+1) ;
}
}
public static void main(String[] args) {
int [] path = new int [12] ;
f(0, path) ;
System.out.println(ans);
}
}
8.取秋博弈
- 两个人取球得游戏,一共有N给球,每人轮流取球,每次可取集合n1,n2,n3中得任何一个数目
- 如果无法继续取球,则游戏结束
- 此时持有奇数个球得人获胜,如果连个都是奇数,则是平局
- 每次都用最聪明得取法
方法1:递归法
import java.util.Arrays;
import java.util.Scanner;
/**
* 8.取秋博弈
* 两个人取球得游戏,一共有N给球,每人轮流取球,每次可取集合n1,n2,n3中得任何一个数目
* 如果无法继续取球,则游戏结束
* 此时持有奇数个球得人获胜,如果连个都是奇数,则是平局
* 每次都用最聪明得取法
*
* 算法思想:只要找出这个最聪明得取法得规律就可以解题
*/
public class Main {
static int [] n = new int [3] ;
static int [] x = new int [5] ;
public static void main(String[] args) {
Scanner input = new Scanner(System.in) ;
for(int i=0; i<n.length; i++){
n[i] = input.nextInt() ;
}
Arrays.sort(n);
//5局,每局球得个数
for(int i=0; i<x.length; i++){
x[i] = input.nextInt() ;
char res = f(x[i], 0, 0) ;
System.out.print(res + " ");
}
System.out.println();
}
/**
* 参数代表当前取球人面临得局面
* @param num 球得总数
* @param me 我方持有得数目
* @param you 对手持有得数目
* @return
*/
private static char f(int num, int me, int you) {
if(num < n[0]){
if((me&1)==1 && (you&1)==0){
return '+' ;
}else if((you&1)==1 &&(me&1)==0){
return '-';
}else{
return '0' ;
}
}
boolean flat = false;
for(int i=0; i<n.length; i++){
if(num >= n[i]){
char res = f(num-n[i], you, me+n[i]) ;
if(res == '-'){
return '+';
}
if(res == '0'){
flat = true ;
}
}
}
if(flat == true){
return '0' ;
}else{
return '-' ;
}
}
}
方法2:记忆性递归:
import java.util.Arrays;
import java.util.Scanner;
/**
* 8.取秋博弈
* 两个人取球得游戏,一共有N给球,每人轮流取球,每次可取集合n1,n2,n3中得任何一个数目
* 如果无法继续取球,则游戏结束
* 此时持有奇数个球得人获胜,如果连个都是奇数,则是平局
* 每次都用最聪明得取法
*
* 算法思想:只要找出这个最聪明得取法得规律就可以解题
*/
public class Main2 {
static int [] n = new int [3] ;
static int [] x = new int [5] ;
static char [][][] cache = new char [1000][1000][1000] ;
public static void main(String[] args) {
Scanner input = new Scanner(System.in) ;
for(int i=0; i<n.length; i++){
n[i] = input.nextInt() ;
}
Arrays.sort(n);
//5局,每局球得个数
for(int i=0; i<x.length; i++){
x[i] = input.nextInt() ;
char res = f(x[i], 0, 0) ;
System.out.print(res + " ");
}
System.out.println();
}
/**
* 参数代表当前取球人面临得局面
* @param num 球得总数
* @param me 我方持有得数目
* @param you 对手持有得数目
* @return
*/
private static char f(int num, int me, int you) {
if(num < n[0]){
if((me&1)==1 && (you&1)==0){
return '+' ;
}else if((you&1)==1 &&(me&1)==0){
return '-';
}else{
return '0' ;
}
}
if(cache[num][me][you] != '\0'){
return cache[num][me][you] ;
}
boolean flat = false;
for(int i=0; i<n.length; i++){
if(num >= n[i]){
char res = f(num-n[i], you, me+n[i]) ;
if(res == '-'){
cache[num][me][you] = '+' ;
return '+';
}
if(res == '0'){
flat = true ;
}
}
}
if(flat == true){
cache[num][me][you] = '0';
return '0' ;
}else{
cache[num][me][you] = '-' ;
return '-' ;
}
}
}
方法3:值考虑奇偶的记忆性递归
import java.util.Scanner;
public class Main1 {
static int [] n = new int [3] ;
static int [] x = new int [5] ;
static char [][][] cache = new char [1000][2][2] ;
public static void main(String[] args) {
Scanner input = new Scanner(System.in) ;
for(int i=0; i<n.length; i++){
n[i] = input.nextInt() ;
}
for(int i=0; i<x.length; i++){
x[i] = input.nextInt() ;
char res = f(x[i], 0, 0) ;
System.out.print(res + " ");
}
System.out.println();
}
/**
* 参数代表当前取球人面临得局面
* @param num 球得总数
* @param me 我方数目得奇偶性
* @param you 我方数目的奇偶性
* @return
*/
private static char f(int num, int me, int you) {
if(num < n[0]){
if((me&1)==1 && (you&1)==0){
return '+' ;
}else if((you&1)==1 && (me&1)==0){
return '-' ;
}else{
return '0' ;
}
}
if(cache[num][me][you] != '\0'){ //记忆性递归,提高效率
return cache[num][me][you] ;
}
boolean flag = false ;
for(int i=0; i<n.length; i++){
if(num >= n[i]){
char res = f(num-n[i], you, (n[i]&1)==0 ? me : 1-me) ; //传递me和you的奇偶性
if(res == '-'){
cache[num][me][you] = '+' ;
return '+' ;
}
if(res == '0'){
flag = true ;
}
}
}
if(flag){
cache[num][me][you] = '0' ;
return '0' ;
}else{
cache[num][me][you] = '-' ;
return '-' ;
}
}
}
9.交换瓶子
- 有N个瓶子,瓶子的编号为1~N,放在架子上,
- 比如有5个瓶子:2,1,3,5,4
- 要求每次拿起两个瓶子,交换它们的顺序,
- 经过若干次后,瓶子的顺序为:1,2,3,4,5
- 对于那么简单的情况,当然交换两次就能解决。
- 如果瓶子更多呢,可以通过编程来解决。
import java.util.Scanner;
/**
* 9.交换瓶子
* 有N个瓶子,瓶子的编号为1~N,放在架子上,
* 比如有5个瓶子:2,1,3,5,4
* 要求每次拿起两个瓶子,交换它们的顺序,
* 经过若干次后,瓶子的顺序为:1,2,3,4,5
* 对于那么简单的情况,当然交换两次就能解决。
* 如果瓶子更多呢,可以通过编程来解决。
*
*/
public class Main {
static int N ;
static int [] arr ;
static int count = 0 ;
public static void main(String[] args) {
Scanner input = new Scanner(System.in) ;
N = input.nextInt() ;
arr = new int [N] ;
for(int i=0; i<N; i++){
arr[i] = input.nextInt() ;
}
for(int i=0; i<N; i++){
for(int j=i+1; j<N; j++){
count += f(arr, i, j) ;
}
}
System.out.println(count) ;
}
private static int f(int [] arr, int i, int j){
for(int x=i; x<N; x++){
for(int y=x+1; y<N; y++){
if(arr[i] != i+1 && arr[j]==i+1) {
swap(arr, i, j) ;
return 1 ;
}
}
}
return 0 ;
}
private static void swap(int []arr, int i, int j){
int temp = arr[i] ;
arr[i] = arr[j] ;
arr[j] = temp ;
}
}
10.压缩变换
- 小明最近在研究压缩算法
- 最近,小明需要压缩一些正整数序列,这些序列的特点是后面出现的数字很大可能是刚出现不久的数字,
- 对于这种特殊的序列,小明准备对序列做一个变换来减小数字的值,
- 变换的过程如下:
- 从左到右枚举序列,每枚举一个数字,如果这个数字没有出现过,则数字变换为它的相反数
- 如果数字出现过,则看它在原序列中最后一次出现后面(且在当前数前面)出现了几种数字,用这个种类替换原来的数字。
import java.util.*;
/**
* 10.压缩变换
* 小明最近在研究压缩算法
* 最近,小明需要压缩一些正整数序列,这些序列的特点是后面出现的数字很大可能是刚出现不久的数字,
* 对于这种特殊的序列,小明准备对序列做一个变换来减小数字的值,
* 变换的过程如下:
* 从左到右枚举序列,每枚举一个数字,如果这个数字没有出现过,则数字变换为它的相反数
* 如果数字出现过,则看它在原序列中最后一次出现后面(且在当前数前面)出现了几种数字,用这个种类替换原来的数字。
*/
public class Main {
static int n ;
static int [] arr ;
static int [] res ;
static Map<Integer, Integer> map ;
static Set<Integer> set = new TreeSet<>();
public static void main(String[] args) {
Scanner input = new Scanner(System.in) ;
n = input.nextInt() ;
arr = new int [n] ;
res = new int [n] ;
map = new HashMap<>() ;
for(int i=0; i<arr.length; i++){
arr[i] = input.nextInt() ;
res[i] = f(arr, i) ;
System.out.print(res[i] + " ");
}
System.out.println();
}
private static int f(int [] arr, int i){
if(map.get(arr[i]) == null){
map.put(arr[i], i) ;
return -arr[i] ;
}else {
int sum = 0;
if(i-map.get(arr[i])==1){
map.put(arr[i], i) ;
return 0 ;
}
for (int j = map.get(arr[i]) + 1; j < i; j++) {
set.add(arr[j]);
}
sum += set.size();
set.clear();
map.put(arr[i], i) ;
return sum ;
}
}
}
优化:线段树法,时间复杂度降为nlogn。
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
public class Main2 {
static int n ;
static int [] a ; //初始数组
static int [] res ;//结果数组
static Map<Integer, Integer> map = new HashMap<>() ; //映射数值与下标
static SegTree root ; //线段树
public static void main(String[] args) {
Scanner input = new Scanner(System.in) ;
n = input.nextInt() ;
a = new int [n] ;
res = new int [n] ;
root = buildSegTree(0, n-1);
for(int i=0; i<n; i++){
a[i] = input.nextInt() ;
Integer preIndex = map.get(a[i]) ;
if(preIndex == null){
res[i] = -a[i] ;
//更新线段树
update(root, i, 1) ;
}else{
//求区区间和,也就是指定区间不同数字的种类数
res[i] = query(root, preIndex+1, i-1) ;
//更新线段树
update(root, preIndex, -1);
update(root, i, 1);
}
map.put(a[i], i) ;
}
for(int i=0; i<res.length; i++){
System.out.print(res[i] + " ");
}
}
private static int query(SegTree root, int x, int y) { //求区间和
int l = root.l ;
int r = root.r ;
if(x <= l && y>=r){
return root.sum ;
}
int mid = (l+r)>>1 ;
int ans = 0 ;
if(x <= mid){
ans += query(root.lson, x, y) ;
}
if(y > mid){
ans += query(root.rson, x, y) ;
}
return ans ;
}
private static void update(SegTree tree, int p, int i) { //更新线段树
if(tree == null){ //出口
return ;
}
int l = tree.l ;
int r = tree.r ;
tree.sum += i ;
int mid = (l+r)>>1 ;
if(p<=mid){
update(tree.lson, p, i);
}else{
update(tree.rson, p, i);
}
}
static SegTree buildSegTree(int l, int r){ //构造线段树
SegTree segTree = new SegTree(l, r) ;
if(l==r){
segTree.sum = 0 ;
return segTree ;
}
int mid = (l + r) >> 1 ;
SegTree lson = buildSegTree(l, mid) ;
SegTree rson = buildSegTree(mid+1, r) ;
segTree.lson = lson;
segTree.rson = rson ;
return segTree ;
}
static class SegTree{
int l, r ; //区间端点
int sum ; //区间和
SegTree lson ; //左子树
SegTree rson ; //右子树
public SegTree(int l, int r) {
this.l = l;
this.r = r;
}
}
}