第十一届蓝桥杯大赛软件类省赛Java研究生组-题解

目录

试题A :约数个数

试题B:跑步锻炼

试题C:平面分割

试题D:蛇形填数

试题E:排序

试题F:成绩统计

试题G:回文日期

试题H:作物杂交

试题I:子串分值

试题J:装饰珠


试题A :约数个数

答案:96

思路:简单枚举,注意1和本身也算约数。

public class Main {
    public static void main(String[] args) {
        int ans = 0 ;
        for(int i=1; i<=78120; i++){
            if(78120%i==0){
                ans ++ ;
            }
        }
        System.out.println(ans);
    }
}

试题B:跑步锻炼

答案:8879

思路:Java日期时间API的应用,根据平年和闰年,算出总天数days,算出周一的天数a,算出初一的天数b,再算出既是初一又是周一的天数ans,那么最终的跑步路程为(days-(a+b-ans)) + (a+b-ans)*2。


import java.util.Calendar;

import static java.time.Year.isLeap;

public class Main {
    public static void main(String[] args) {
        Calendar c = Calendar.getInstance() ;
        int years = 2000;
        int same = 0 ;
        //Calendar的月份是从0到11的,不是1到12,需要注意,谨慎一点,时间API也是常考的
        c.set(years,0, 1) ;

        while(c.get(Calendar.YEAR) != 2020 || c.get(Calendar.MONTH) != 9 || c.get(Calendar.DATE) != 1){
            //从2000年1月1日开始判断,每次判断一个月的第一天,如果是周一,就用same记录
            int weekday = c.get(Calendar.DAY_OF_WEEK) - 1 ;
            if(weekday==1){
                same ++ ;
            }

            if(c.get(Calendar.MONTH)<11){
                c.set(Calendar.MONTH, c.get(Calendar.MONTH)+1) ;
            }else{
                years ++ ;
                c.set(Calendar.YEAR, years);
                c.set(Calendar.MONTH, 0);
            }
        }


        long days = 0 ;
        for(int year=2000; year<=2020; year++){
            if(isLeap(year)){
                days += 366 ;
            }else{
                days += 365 ;
            }
        }

        days = days - 31 - 30 - 30 ;
        long a = (days-6) / 7 + 1; //星期1
        long b = 12*20 + 10  ; //初一

        long ans = days - (a+b-same) ;

        System.out.println(2*(a+b-same)+ans);

    }
}

试题C:平面分割

答案:1391

思路:没有思路,哈哈,我看别人的思路是让尽可能的相交,这样产生的区域就越多,至于具体怎么推导的,可以看下这个。参考链接:蓝桥杯试题E: 平面分割 - riz9 - 博客园

试题D:蛇形填数

答案:761

思路:模拟斜对角向上和向下填数过程即可。

//试题D-蛇形填数
public class Main {
    public static void main(String[] args) {
        int row = 0, col = 0 ;
        int num = 0 ;
        while(true){
            num ++ ;

            if(row==19 && col ==19){
                break ;
            }

            if((row+col)%2==0){ //向上走
                if(row==0){
                    col ++ ;
                }else{
                    row -- ;
                    col ++ ;
                }
            }else{ //向下走
                if(col==0){
                    row ++ ;
                }else{
                    row ++ ;
                    col -- ;
                }
            }

        }
        System.out.println(num);
    }
}

试题E:排序

答案:jonmlkihgfedcba

相邻交换100次,最短需要15个字符,正常前15个字符逆序,交换1+2+3+...+14=105次,故把j放到首位,交换100次,最短并且字典序最小。

public class Main {
    public static void main(String[] args) {
        int ans = 0 ;
        //前15个小写字母逆序,执行105交换,此时是最短的,要保证字典序最小,把第5个移动到首位
        System.out.println("jonmlkihgfedcba");
    }
}

试题F:成绩统计

思路:枚举,计算即可,注意四舍五入。

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in) ;
        int n =input.nextInt() ;
        int pass = 0, excellent = 0 ;
        for(int i=0; i<n; i++){
            int grade = input.nextInt() ;
            if(grade>=85){
                excellent ++ ;
            }
            if(grade>=60){
                pass++ ;
            }
        }
        int ans1, ans2 ;
        double x = 1.0 * pass / n * 1000;
        double y = 1.0 * excellent / n * 1000 ;
        if(x==1000){
            System.out.println(100 + "%");
        }else {
            String s1 = String.valueOf(x);
            int c1 = s1.charAt(2) - '0';
            if (c1 >= 5) {
                ans1 = Integer.parseInt(s1.substring(0, 2)) + 1;
            } else {
                ans1 = Integer.parseInt(s1.substring(0, 2));
            }
            System.out.println(ans1 + "%");
        }
        if(y==1000){
            System.out.println(100 + "%");
        }else {
            String s2 = String.valueOf(y);
            int c2 = s2.charAt(2) - '0';
            if (c2 >= 5) {
                ans2 = Integer.parseInt(s2.substring(0, 2)) + 1;
            } else {
                ans2 = Integer.parseInt(s2.substring(0, 2));
            }
            System.out.println(ans2 + "%");
        }
    }
}

试题G:回文日期

思路:枚举N+1天开始的所有日期,判断是否满足条件即可。细节很多,需要注意处理细节。

import java.util.Scanner;
import static java.time.Year.isLeap ;


public class Main {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in) ;
        int N = input.nextInt() ;

        for(int i=N+1;i<=99991231;i++){
            int d = i % 100 ;
            if(d>31){
                i += 68 ;
            }
            if(i%10000>=13000){
                i += i / 10000 + 999 ;
            }
            if(f1(i)){
                System.out.println(i);
                break ;
            }
        }
        for(int i=N+1;i<=99991231; i++){
            int d = i % 100 ;
            if(d>31){
                i += 68 ;
            }
            if(i%10000>=13000){
                i += i / 10000 + 999;
            }
            if(f1(i) && f2(i)){
                System.out.println(i);
                break ;
            }
        }
    }
    public static boolean f1(int i){
        String s = String.valueOf(i) ;
        int month = Integer.parseInt(s.substring(4,6)) ;
        int day = Integer.parseInt(s.substring(6,8)) ;
        if(month==0 || day==0){
            return false ;
        }
        if(month>12 || day>31){
            return false ;
        }
        if(month==4 || month==6 || month==9 || month==11 ){
            if(day>30){
                return false ;
            }
        }
        if(isLeap(Integer.parseInt(s.substring(0,4)))){
            if(day>29){
                return false ;
            }
        }else{
            if(day>28){
                return false ;
            }
        }
        if(!isPalindrome(s)){
            return false ;
        }
        return true ;
     }

    private static boolean isPalindrome(String s) {
        for(int i=0; i<s.length(); i++){
            if(s.charAt(i) != s.charAt(s.length()-1-i)){
                return false ;
            }
        }
        return true ;
    }

    public static boolean f2(int i){
        String s = String.valueOf(i) ;
        if(s.charAt(0)==s.charAt(2) && s.charAt(2) == s.charAt(5) && s.charAt(7)==s.charAt(5)){
            if(s.charAt(1)==s.charAt(3) && s.charAt(3) == s.charAt(4) && s.charAt(4)==s.charAt(6)){
                if(s.charAt(0) != s.charAt(1)){
                    return true ;
                }
            }
        }
        return false ;
    }
}

试题H:作物杂交

思路:记忆化搜索,ans数组记忆已经得到的结果,vis数组标记当前作物是否已经生成,如果目标种子没有合成,遍历当前所有已有的杂交组合,对于合成值等于target的,递归计算两个合成target值得种子分别合成所需要得最大值。求出所有可能得最小值即是答案。AC代码如下:


import java.util.Scanner;

public class Main {
    static int N, M, K, T ;
    static int [][] hby ;
    static int [] time ;
    static int [] seed ;
    static int [] maxTime ;
    static int [] vis ;
    static int [] ans ;
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in) ;
        N = input.nextInt() ;
        M = input.nextInt() ;
        K = input.nextInt() ;
        T = input.nextInt() ;
        time = new int [N+1] ;
        seed = new int [M] ;
        hby = new int [K][3] ;
        maxTime = new int [K] ; //记录两两杂交的最大时间
        vis = new int [N+1] ;
        ans = new int [N+1] ;


        for(int i=1; i<=N; i++){
            time[i] = input.nextInt() ;
        }
        for(int i=0; i<M; i++){
            seed[i] = input.nextInt() ;
            vis[seed[i]] = 1 ; //记录作物已经合成
        }

        for(int i=0; i<K; i++){
            hby[i][0] = input.nextInt() ;
            hby[i][1] = input.nextInt() ;
            hby[i][2] = input.nextInt() ;
            maxTime[i] = Math.max(time[hby[i][0]], time[hby[i][1]]) ;
        }
        System.out.println(dfs(T));
    }

    private static int  dfs(int target) {
        if(vis[target]!=1){
            int min = Integer.MAX_VALUE ;
            for(int i=0; i<K; i++){
                if(hby[i][2]==target){
                    min = Math.min(min, maxTime[i] + Math.max(dfs(hby[i][0]), dfs(hby[i][1]))) ;
                }
            }
            vis[target] = 1 ;
            ans[target] = min ;
            return min ;
        }else{
            return ans[target] ;
        }
    }
}

试题I:子串分值

思路1:枚举+HashMap记录,枚举所有可能,Map记录每个出现1次的字符,统计个数。可以通过部分测试用例,拿到10分+没问题。

import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;


public class Main {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in) ;
        String s = input.next() ;
        long ans = 0 ;
        for(int i=0; i<s.length(); i++){
            for(int j=i+1; j<=s.length(); j++){
               ans += f(s.substring(i,j));
                }
            }
        System.out.println(ans);
    }
    public static long f(String s){
        long sum = 0 ;
        Map<Character, Integer> map = new HashMap<>() ;
        for(int i=0; i<s.length(); i++){
            map.put(s.charAt(i), map.getOrDefault(s.charAt(i), 0) + 1) ;
        }
        for(char c : map.keySet()){
            if(map.get(c)==1){
                sum ++ ;
            }
        }
        return sum ;
    }
}

思路2:AC代码,动态规划思想,dp[i]表示以i结尾的字符串中的每个字符的贡献值,place数组记录每个字符上一次出现的位置,ans记录当前字符的贡献值,sum为所有字符的贡献值,每一次更新dp[c]和place[c].


import java.util.*;

public class Main1 {
    static int ans, sum ;
    static int [] dp ;
    static int [] place ;
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in) ;
        String s = input.next() ;
        dp = new int [26] ; //dp[i]表示以i结尾的字符串中每个字符的贡献值
        place = new int [26] ; //记录每个字符出现的位置

        Arrays.fill(place, -1) ;

        for(int i=0; i<s.length(); i++){
            int c = s.charAt(i) - 'a' ;
            ans += i - place[c] - dp[c] ;
            sum += ans ;
            dp[c] = i - place[c] ;
            place[c] = i ;
        }
        System.out.println(sum);
    }
}

试题J:装饰珠

参考链接:算法练习题27---蓝桥杯2020省赛“装饰珠”_杨大熊的代码世界的博客-CSDN博客_蓝桥杯 装饰珠

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

nuist__NJUPT

给个鼓励吧,谢谢你

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

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

打赏作者

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

抵扣说明:

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

余额充值