第七届蓝桥杯(2016年)JavaA组省赛真题解析

第七届蓝桥杯(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;
        }
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

nuist__NJUPT

给个鼓励吧,谢谢你

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值