【蓝桥】算法真题【二】


后缀表达式

  • 题目:

    给定 N 个加号、M 个减号以及 N + M + 1 个整数 A1,A2,··· ,AN+M+1,小 明想知道在所有由这 N 个加号、M 个减号以及 N + M +1 个整数凑出的合法的后缀表达式中,结果最大的是哪一个?

    请你输出这个最大的结果。

    例如使用1 2 3 + -,则 “2 3 + 1 -” 这个后缀表达式结果是 4,是最大的。

    【输入格式】

    第一行包含两个整数 N 和 M。 第二行包含 N + M + 1 个整数 A1,A2,··· ,AN+M+1。

    【输出格式】

    输出一个整数,代表答案。

  • 思路:

    看到后缀表达式,我一开始还真把样例输入的树型在草稿纸上画了出来,还想着是不是要求出所有的树形,然后用中序遍历求和……后面才发现想得太复杂了,这题应该属于考场上的简单题吧,不是考数据结构,而是考找规律。

    • 如果全都是加号(m==0),那就把所有数字相加。
    • 如果有减号(m!=0),有这样一个规律:加上最大的数,减去最小的数(long odd=arr.get(arr.size()-1)-arr.get(0);这段代码的来源),再加上其他数的绝对值能得出正确结果。
  • 题解:

    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.Scanner;
    
    public class Main {
        public static void main(String[] args) {
     Scanner s=new Scanner(System.in);
    long n=s.nextInt();
    long m=s.nextInt();
    long len=n+m+1;
            ArrayList<Integer> arr=new ArrayList<>();
            for (int i = 0; i <len ; i++) {
                arr.add(s.nextInt());
            }
    Collections.sort(arr);//升序
            long sum=0;
            if(m==0){
                 sum=0;
                for (int i = 0; i <arr.size() ; i++) {
                    sum+=arr.get(i);
                }
            }else{
                long odd=arr.get(arr.size()-1)-arr.get(0);
                 sum=odd;
                for (int i = 1; i <arr.size()-1 ; i++) {
                   sum+=Math.abs(arr.get(i));
                }
            }
            System.out.println(sum);
        }
    }
    

从写下面这题开始正式入坑AcWing这个平台,有思路和题解在平台里面写,之后这边的更新应该会少了。


括号序列

给定一个括号序列,要求尽可能少地添加若干括号使得括号序列变得合法,当添加完成后,会产生不同的添加结果,请问有多少种本质不同的添加结果。

两个结果是本质不同的是指存在某个位置一个结果是左括号,而另一个是右括号。

例如,对于括号序列 (((),只需要添加两个括号就能让其合法,有以下几种不同的添加结果:()()()、()(())、(())()、(()())((()))

  • 输入格式

输入一行包含一个字符串 ss,表示给定的括号序列,序列中只有左括号和右括号。

  • 输出格式

输出一个整数表示答案,答案可能很大,请输出答案除以 10000000071000000007 (即 109+7109+7) 的余数。

  • 数据范围

对于 40%40% 的评测用例,|s|≤200|s|≤200。
对于所有评测用例,1≤|s|≤50001≤|s|≤5000。

  • 输入样例:
((()
  • 输出样例:
5
  • yxc的视频思路:动态规划、枚举

    • 左括号总数与右括号总数应相等

    • 前缀中左括号数不小于右括号数

    • f ( i , j ) = f ( i − 1 , j + 1 ) + f ( i − 1 , j ) + f ( i − 1 , j − 1 ) + … … + f ( i − 1 , 0 ) f ( i , j − 1 ) = f ( i − 1 , j ) + f ( i − 1 , j − 1 ) + … … + f ( i − 1 , 0 ) f ( i , j ) = f ( i − 1 , j + 1 ) + f ( i , j − 1 ) f(i,j)=f(i-1,j+1)+f(i-1,j)+f(i-1,j-1)+……+f(i-1,0)\\ f(i,j-1)=f(i-1,j)+f(i-1,j-1)+……+f(i-1,0)\\ f(i,j)=f(i-1,j+1)+f(i,j-1) f(i,j)=f(i1,j+1)+f(i1,j)+f(i1,j1)++f(i1,0)f(i,j1)=f(i1,j)+f(i1,j1)++f(i1,0)f(i,j)=f(i1,j+1)+f(i,j1)

  • c++题解:

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

typedef long long ll;
const int N=5010;
int n;
char str[N];
ll f[N][N];
const int MOD=1e9 + 7;

ll calc() {
	memset(f,0,sizeof f);
	f[0][0]=1;
	for(int i=1; i<=n; i++) {
		if(str[i]=='(') {

			for(int j=1; j<=n; j++) {
				f[i][j]=f[i-1][j-1];
			}
		} else {
			f[i][0]=(f[i-1][0]+f[i-1][1])%MOD;
			for(int j=1; j<=n; j++) {
				f[i][j]=(f[i-1][j+1]+f[i][j-1])%MOD;
			}
		}
	}
	for(int i=0;i<=n;i++){
		if(f[n][i]) return f[n][i];
	}
	 return -1;
}

int main() {
	scanf("%s",str+1);
	n=strlen(str+1);
	ll left=calc();
	reverse(str+1,str+n+1);
	for(int i=1; i<=n; i++) {
		if(str[i]=='(') str[i]=')';
		else str[i]='(';
	}
	ll right=calc();
	printf("%lld\n",left*right%MOD);
	return 0;
}

砝码称重

  • 题目:

    你有一架天平和 NN 个砝码,这 NN 个砝码重量依次是 W1,W2,⋅⋅⋅,WNW1,W2,···,WN。

    请你计算一共可以称出多少种不同的正整数重量?

    注意砝码可以放在天平两边。

    输入格式

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

    第二行包含 NN 个整数:W1,W2,W3,⋅⋅⋅,WNW1,W2,W3,···,WN。

    输出格式

    输出一个整数代表答案。

    数据范围

    对于 50%50% 的评测用例,1≤N≤151≤N≤15。
    对于所有评测用例,1≤N≤1001≤N≤100,NN 个砝码总重不超过 105105。

    输入样例:
    3
    1 4 6
    
    输出样例:
    10
    
  • 思路关键字:背包问题,动态规划

    对于第i个砝码,它有三种可能。

    • 没被选中,则前i-1个中选出的砝码质量要达到 j

    • 在天平左边(+),则前i-1个中选出的砝码质量要达到j-w[i]

    • 在天平右边(-),则前i-1个中选出的砝码质量要达到j+w[i]

      这三个状态共同构成一个集合,只要满足其中一个状态且该状态不为空,该集合就不为空(f[] [] =true)

  • 题解:

    import java.util.Scanner;
    
    public class Main {
        private static int n,m;//砝码个数,砝码总重量
        private static int N=110,M=200010,bias=M/2;//数组下标不能出现负数,所以设个偏移量
        //如原来是-m~m,变为m~2m
        public static void main(String[] args) {
            boolean [][] f=new boolean[N][M];//判断是否存在
            int [] w=new int[N];
     Scanner s=new Scanner(System.in);
     n=s.nextInt();
            for (int i = 1; i <=n ; i++) {
                w[i]=s.nextInt();
                m+=w[i];
            }
    
            f[0][bias]=true;
            for (int i = 1; i <=n ; i++) {
                for (int j = -m; j <=m ; j++) {
                    f[i][j+bias]=f[i-1][j+bias];
                    if(j-w[i]>=-m) f[i][j+bias] |=f[i-1][j+bias-w[i]];
                    if(j+w[i]<=m) f[i][j+bias] |=f[i-1][j+bias+w[i]];
    
                }
            }
            int sum=0;
            for (int i = 1; i <=m ; i++) {
    if(f[n][i+bias]) sum++;
            }
            System.out.println(sum);
        }
    }
    

回文日期

  • 题目:

    20202020 年春节期间,有一个特殊的日期引起了大家的注意:20202020 年 22 月 22 日。

    因为如果将这个日期按 “yyyymmdd” 的格式写成一个 88 位数是 20200202,恰好是一个回文数。

    我们称这样的日期是回文日期。

    有人表示 20200202 是“千年一遇” 的特殊日子。

    对此小明很不认同,因为不到 22 年之后就是下一个回文日期:20211202 即 20212021 年 1212 月 22 日。

    也有人表示 20200202 并不仅仅是一个回文日期,还是一个 ABABBABA 型的回文日期。

    对此小明也不认同,因为大约 100100 年后就能遇到下一个 ABABBABA 型的回文日期:21211212 即 21212121 年 1212 月 1212 日。

    算不上“千年一遇”,顶多算“千年两遇”。

    给定一个 88 位数的日期,请你计算该日期之后下一个回文日期和下一个 ABABBABA 型的回文日期各是哪一天。

    注意

    下一个回文日期和下一个 ABABBABA 型的回文日期可能是同一天。

    ABABBABA 型的回文日期,需要满足 A≠BA≠B。

    输入格式

    输入包含一个八位整数 NN,表示日期。

    输出格式

    输出两行,每行 11 个八位数。

    第一行表示下一个回文日期,第二行表示下一个 ABABBABA 型的回文日期。

    数据范围

    对于所有评测用例,10000101≤N≤8999123110000101≤N≤89991231,保证 NN 是一个合法日期的 88 位数表示。

    输入样例:
    20200202
    
    输出样例:
    20211202
    21211212
    
    难度:简单
    时/空限制:1s / 64MB
    总通过数:915
    总尝试数:3306
    来源:第十一届蓝桥杯省赛第二场C++A/B/研究生组,第十一届蓝桥杯省赛第二场JAVAA/研究生组
    算法标签
  • 题解:

    注:仅能通过80%的测试样例,且超时了,正在想办法优化。

    import java.util.Scanner;
    import java.lang.StringBuffer;
    
    public class Main {
        public static void main(String[] args) {
            Scanner s = new Scanner(System.in);
            long n = s.nextLong();
            boolean first=false;boolean second=false;
            for (long i = n + 1; i <= 99999999; i++) {
                long t = i;
                String T = String.valueOf(t);
                String R = new StringBuffer(T).reverse().toString();
                String year = new StringBuilder(T).substring(0,4);
                String month = new StringBuilder(T).substring(4,6);
                String day = new StringBuilder(T).substring(6);
                if (T.equals(R)) {
                    int intyear = Integer.parseInt(year);
                    int intmonth = Integer.parseInt(month);
                    int intday = Integer.parseInt(day);
                    if (intmonth <= 12&&intmonth>0) {
                        if (intmonth == 1 || intmonth == 3 || intmonth == 5 || intmonth == 7 || intmonth == 8 || intmonth == 10 || intmonth == 12) {
                            if (intday <= 31) {
                                if(first==false)
                                {System.out.println(t);
                                first=true;}
                                if((T.charAt(0) == T.charAt(2)) && (T.charAt(1) == T.charAt(3)) && (T.charAt(0) != T.charAt(1))){
                                    if(second==false)
                                    {System.out.println(t);
                                    second=true;}
                                }
                                if(first==true&&second==false)
                                    continue;
                                if(first==true&&second==true)
                                    break;
                            }
                        }
                        else if(intmonth==2){
                            if((intyear%4==0)&&(intyear%400==0)&&(intyear%400!=0)){
                                if (intday <= 29) {
                                if(first==false)
                                {System.out.println(t);
                                first=true;}
                                if((T.charAt(0) == T.charAt(2)) && (T.charAt(1) == T.charAt(3)) && (T.charAt(0) != T.charAt(1))){
                                    if(second==false)
                                    {System.out.println(t);
                                    second=true;}
                                }
                                if(first==true&&second==false)
                                    continue;
                                if(first==true&&second==true)
                                    break;
                                }
                            }
                            else{
                                if (intday <= 28) {
                                if(first==false)
                                {System.out.println(t);
                                first=true;}
                                if((T.charAt(0) == T.charAt(2)) && (T.charAt(1) == T.charAt(3)) && (T.charAt(0) != T.charAt(1))){
                                    if(second==false)
                                    {System.out.println(t);
                                    second=true;}
                                }
                                if(first==true&&second==false)
                                    continue;
                                if(first==true&&second==true)
                                    break;
                                }
                            }
                        }
                        else{
                            if (intday <= 30) {
                                 if(first==false)
                                {System.out.println(t);
                                first=true;}
                                if((T.charAt(0) == T.charAt(2)) && (T.charAt(1) == T.charAt(3)) && (T.charAt(0) != T.charAt(1))){
                                    if(second==false)
                                    {System.out.println(t);
                                    second=true;}
                                }
                                if(first==true&&second==false)
                                    continue;
                                if(first==true&&second==true)
                                    break;
                            }
                        }
                    }
                }
                
            }
        }
    }
    

成绩分析

题目描述:给定学生总人数,每个学生的成绩,求最高分、最低分、平均分(保留两位小数)

题解:

import java.util.Scanner;
public class Main{
    public static void main(String[] args){
        Scanner s=new Scanner(System.in);
        int n=s.nextInt();
        int [] arr=new int [n];
        int sum=0;
        for(int i=0;i<n;i++){
            arr[i]=s.nextInt();
            sum+=arr[i];
        }
        int max=arr[0];int min=arr[0];
        for(int i=0;i<n;i++){
            if(arr[i]>=max)
            max=arr[i];
            if(arr[i]<=min)
            min=arr[i];
        }
        System.out.println(max);
        System.out.println(min);
        System.out.printf("%.2f",(double)sum/n);
    }
}

特殊日期

  • 今年是2021 年,2021 这个数字非常特殊,它的千位和十位相等,个位比百位大 11,我们称满足这样条件的年份为特殊年份。

    输入 55 个年份,请计算这里面有多少个特殊年份。

    输入格式

    输入 55 行,每行一个 44 位十进制数(数值范围为 1000 至 9999),表示一个年份。

    输出格式

    输出一个整数,表示输入的 55 个年份中有多少个特殊年份。

    输入样例:
    2019
    2021
    1920
    2120
    9899
    
    输出样例:
    2
    
    样例解释

    2021 和 9899 是特殊年份,其它不是特殊年份。

  • 题解:

    import java.util.Scanner;
    public class Main{
        public static void main(String[] args){
             Scanner s=new Scanner(System.in);
            int sum=0;int i=0;
        while(i<5){
            int tmp=s.nextInt();
            int thou=tmp/1000;
            int hun=tmp/100%10;
            int ten=tmp/10%10;
            int num=tmp%10;
            if((thou==ten)&&(hun+1==num)) sum++;
            i++;
        }
        System.out.println(sum);
        }
    }
    

错误票据

  • 题目:

    某涉密单位下发了某种票据,并要在年终全部收回。

    每张票据有唯一的ID号。

    全年所有票据的ID号是连续的,但ID的开始数码是随机选定的。

    因为工作人员疏忽,在录入ID号的时候发生了一处错误,造成了某个ID断号,另外一个ID重号。

    你的任务是通过编程,找出断号的ID和重号的ID。

    假设断号不可能发生在最大和最小号。

    输入格式

    第一行包含整数 NN,表示后面共有 NN 行数据。

    接下来 NN 行,每行包含空格分开的若干个(不大于100个)正整数(不大于100000),每个整数代表一个ID号。

    输出格式

    要求程序输出1行,含两个整数 m,nm,n,用空格分隔。

    其中,mm表示断号ID,nn表示重号ID。

    数据范围

    1≤N≤1001≤N≤100

    输入样例:
    2
    5 6 8 11 9 
    10 12 9
    
    输出样例:
    7 9
    
  • 题解:

    import java.util.*;
    public class Main{
        public static void main(String[] args){
            int [] arr=new int[10000];
            Scanner s=new Scanner(System.in);
            int N=s.nextInt();int num=0;
              int chong=0,duan=0;
          while(s.hasNext()){
              arr[num]=s.nextInt();
              num++;
          }//num 实际读入的数据个数
          Arrays.sort(arr,0,num);
          for(int i=0;i<num-1;i++){
              if(arr[i]==arr[i+1]){
                  chong=arr[i];
              }
              if((arr[i]+2)==arr[i+1]){
                  duan=arr[i]+1;
              }
          }
          System.out.print(duan+" "+chong);
        }
    }
    

买不到的数目(数学定理)

  • 题解:

  • 思路:数学定理:设p,q为正整数且互质,则xp+yq不存在的最大的值为(p-1)(q-1)-1

  • 题解:

    import java.util.*;
    public class Main{
        public static void main(String[] args){
            Scanner s=new Scanner(System.in);
            int p=s.nextInt();
            int q=s.nextInt();
            System.out.println((p-1)*(q-1)-1);
    
        }
    }
    

翻硬币(递推)

  • 题目:

  • 小明正在玩一个“翻硬币”的游戏。

    桌上放着排成一排的若干硬币。我们用 * 表示正面,用 o 表示反面(是小写字母,不是零)。

    比如,可能情形是:**oo***oooo

    如果同时翻转左边的两个硬币,则变为:oooo***oooo

    现在小明的问题是:如果已知了初始状态和要达到的目标状态,每次只能同时翻转相邻的两个硬币,那么对特定的局面,最少要翻动多少次呢?

    我们约定:把翻动相邻的两个硬币叫做一步操作。

    输入格式

    两行等长的字符串,分别表示初始状态和要达到的目标状态。

    输出格式

    一个整数,表示最小操作步数

    数据范围

    输入字符串的长度均不超过100。
    数据保证答案一定有解。

    输入样例1:
    **********
    o****o****
    
    输出样例1:
    5
    
    输入样例2:
    *o**o***o***
    *o***o**o***
    
    输出样例2:
    1
    
  • 题解:

import java.util.*;
public class Main{
    public static void main(String[] args){
        Scanner s=new Scanner(System.in);
String o=s.next();
String g=s.next();
char [] oa=o.toCharArray();
char [] ga=g.toCharArray(); 
int res=0;
for(int i=0;i<o.length()-1;i++){
    if(oa[i]!=ga[i]){
        
        change(oa,i);
        change(oa,i+1);
        res++;
    }
}
System.out.println(res);
    }
    
    public static void change(char [] oa,int i){
      if(oa[i]=='*') oa[i]='o';
      else oa[i]='*';
    }
}

带分数

  • 题目:

    100 可以表示为带分数的形式:100=3+69258/714

    还可以表示为:100=82+3546/197

    注意特征:带分数中,数字 1∼9分别出现且只出现一次(不包含 00)。

    类似这样的带分数,100有 11 种表示法。

    输入格式

    一个正整数。

    输出格式

    输出输入数字用数码 1∼9不重复不遗漏地组成带分数表示的全部种数。

    数据范围

    1≤N<106

    输入样例1:
    100
    
    输出样例1:
    11
    
    输入样例2:
    105
    
    输出样例2:

    6

  • 题解:

    import java.util.*;
    public class Main{
        static long n;
       static int ans=0;
        static int [] had_used=new int [20];
       static  int [] ever=new int [20];
        
        public static void dfs_a(int x,int a){
            if(a>=n) return ;
            dfs_c(x,a,0);//0是c
            for(int i=1;i<=9;i++){
                if(had_used[i]==0){
                    had_used[i]=1;
                    dfs_a(x+1,10*a+i);
                    had_used[i]=0;//回溯
                }
            }
        }
        
        public static void dfs_c(int x,int a,int c){
            if(x>=10) return ;
            if(check(a,c)) ans++;
            for(int i=1;i<=9;i++){
                if(had_used[i]==0){
                    had_used[i]=1;
                    dfs_c(x+1,a,10*c+i);
                    had_used[i]=0;//回溯
                }
            }
            
        }
        
        public static boolean check(long a,long c){
            long b=c*n-c*a;
            if(a==0||b==0||c==0) return false;
            for(int i=1;i<=9;i++)
                ever[i]=had_used[i];
                
                while(b>0){
                    long wei=b%10;
                    b/=10;
                    if((wei==0)||ever[(int)wei]==1) return false;
                    ever[(int)wei]=1;
                }
            
            
            for(int i=1;i<=9;i++){
                if(ever[i]==0) return false;
            }
            return true;
        }
        
        public static void main(String[] args){
            Scanner s=new Scanner(System.in);
     n=s.nextLong();
    dfs_a(0,0);//已用0个数,当前数总值为0
    System.out.println(ans);
        }
    }
    

蚂蚁感冒

  • 题目:

    长 100厘米的细长直杆子上有 n 只蚂蚁。

    它们的头有的朝左,有的朝右。

    每只蚂蚁都只能沿着杆子向前爬,速度是 1 厘米/秒。

    当两只蚂蚁碰面时,它们会同时掉头往相反的方向爬行。

    这些蚂蚁中,有 1只蚂蚁感冒了。

    并且在和其它蚂蚁碰面时,会把感冒传染给碰到的蚂蚁。

    请你计算,当所有蚂蚁都爬离杆子时,有多少只蚂蚁患上了感冒。

    输入格式

    第一行输入一个整数 n, 表示蚂蚁的总数。

    接着的一行是 n 个用空格分开的整数 Xi, Xi 的绝对值表示蚂蚁离开杆子左边端点的距离。

    正值表示头朝右,负值表示头朝左,数据中不会出现 0 值,也不会出现两只蚂蚁占用同一位置。

    其中,第一个数据代表的蚂蚁感冒了。

    输出格式

    输出1个整数,表示最后感冒蚂蚁的数目。

    数据范围

    1<n<50
    0<|Xi|<100

    输入样例1:
    3
    5 -2 8
    
    输出样例1:
    1
    
    输入样例2:
    5
    -10 8 -20 12 25
    
    输出样例2:
    3
    
  • 题解:

    import java.util.*;
    
    public class Main{
        public static void main(String[] args){
            Scanner s=new Scanner(System.in);
            int n=s.nextInt();
            int first=s.nextInt(),left=0,right=0;
            for(int i=1;i<n;i++){
              int tmp=s.nextInt();
              if(tmp<0&&Math.abs(tmp)>Math.abs(first)) right++;//在第一只右边想往左走
              if(tmp>0&&Math.abs(tmp)<Math.abs(first)) left++;//在第一只左边想往右走
            }
    if((first>0&&right==0)||(first<0&&left==0)) System.out.println("1");
    else System.out.println(left+right+1);
        }
    }
    

饮料换购

  • 题目:

  • 思路:最终所得的饮料数由两部分构成:

    • 原始饮料数
    • 3瓶盖换一瓶新的等价于少了两个瓶盖
  • 题解:

    import java.util.*;
    
    public class Main{
        public static void main(String[] args){
            Scanner s=new Scanner(System.in);
            int n=s.nextInt();
            int sum=n;
    while(n>=3){
        n-=2;
        sum++;
    }
    System.out.println(sum);
        }
    }
    

移动距离

  • 题解:

    import java.util.*;
    
    public class Main{
        public static void main(String[] args){
            Scanner s=new Scanner(System.in);
            int w=s.nextInt();
            int m=s.nextInt()-1;
            int n=s.nextInt()-1;
            int x1=m/w;int x2=n/w;
            int y1=m%w;int y2=n%w;
            if(x1%2!=0) y1=w-1-y1;
            if(x2%2!=0) y2=w-1-y2;
            System.out.println(Math.abs(x1-x2)+Math.abs(y1-y2));
        }
    }
    
  • 代码解释:

    • m、n为获取值-1是为了与行列下标一致(默认从0开始)

    • x是行下标,y是列下标

    • 奇数行是倒序

    • 关于奇数的判断:在c++中,1和true等价,故可以用(x1&1)来判断,但是java的int类型和boolean类型不能比较,也不能强制转换,我就用了更简单的%2!=0判断

    • 关于& :与 的位运算,将两个比较的数化为二进制,比较每一位对应的数,都是1则为1,其余为0


      00001111 00001111 00001111


11110001 11110001 11110001
的&结果为1(00000001)

四平方和

  • 题目:

  • 思路:给定的N可以表示为:N=m2+n2+i2+j2

    要求的输出是mnij从小到大,可以枚举所有的情况,从i和j这两个较大的数开始,并把结果放在集合1(arr1)中;第二步枚举较小的数,N-m2+n2其实等价于i2+j2,如果能在集合1对应位置找到该值,说明这是一种情况,(而且由于是从较大的数开始枚举的,输出的第一种情况就是题目要求的第一种表示),输出后退出。(c++中return 0;java就用System.exit(0)😉

  • 题解:

    import java.util.*;
    public class Main{
        public static void main(String[] args){
            Scanner s=new Scanner(System.in);
            int N=s.nextInt();
            int [] arr1=new int [5000010];
            int [] arr2=new int [5000010];
            //将数组所有元素初始化为-1,
            for(int i=0;i<5000010;i++){
                arr1[i]=-1;
            }
            //枚举i j
    for(int i=0;i*i<=N;i++){
        for(int j=i;i*i+j*j<=N;j++){
            int sum=i*i+j*j;
            if(arr1[sum]==-1){
                arr1[sum]=i;
                arr2[sum]=j;
            }
        }
    }
    
    for(int m=0;m*m<=N;m++){
        for(int n=m;m*m+n*n<=N;n++){
            int sum=N-m*m-n*n;
            if(arr1[sum]!=-1){
               System.out.print(m+" "+n+" "+arr1[sum]+" "+arr2[sum]);//m,n,i,j
               System.exit(0);
            }
        }
    }
    
        }
    }
    

日期问题

  • 题目:

    小明正在整理一批历史文献。这些历史文献中出现了很多日期。

    小明知道这些日期都在1960年1月1日至2059年12月31日。

    令小明头疼的是,这些日期采用的格式非常不统一,有采用年/月/日的,有采用月/日/年的,还有采用日/月/年的。

    更加麻烦的是,年份也都省略了前两位,使得文献上的一个日期,存在很多可能的日期与其对应。

    比如02/03/04,可能是2002年03月04日、2004年02月03日或2004年03月02日。

    给出一个文献上的日期,你能帮助小明判断有哪些可能的日期对其对应吗?

    输入格式

    一个日期,格式是”AA/BB/CC”。

    即每个’/’隔开的部分由两个 0-9 之间的数字(不一定相同)组成。

    输出格式

    输出若干个不相同的日期,每个日期一行,格式是”yyyy-MM-dd”。

    多个日期按从早到晚排列。

    数据范围

    0≤A,B,C≤90≤A,B,C≤9

    输入样例:
    02/03/04
    
    输出样例:
    2002-03-04
    2004-02-03
    2004-03-02
    
  • 题解:

    //超过12不可能是月,超过31不可能是日
    //年:平年闰年 月:31日和30日
    //年月日 月日年 日月年 符合要求则输出,否则不做
    import java.util.*;
    public class Main{
       static int [] Months={31,28,31,30,31,30,31,31,30,31,30,31};//使用的时候记得月数减1与下标对应
        public static void main(String[] args){
        Scanner s=new Scanner(System.in);
         String date=s.next();
         String [] goal=date.split("/");
         int a,b,c;
     a=Integer.parseInt(goal[0]);
      b=Integer.parseInt(goal[1]);
       c=Integer.parseInt(goal[2]);
    ArrayList<Integer> list=new ArrayList<>();
    
    int tmp=0;
    //year month day
    tmp=check(1900+a,b,c);
    if(tmp!=0&&list.contains(tmp)==false) list.add(tmp);
    tmp=check(2000+a,b,c);
    if(tmp!=0&&list.contains(tmp)==false) list.add(tmp);
    //month,day,year
    tmp=check(1900+c,a,b);
    if(tmp!=0&&list.contains(tmp)==false) list.add(tmp);
    tmp=check(2000+c,a,b);
    if(tmp!=0&&list.contains(tmp)==false) list.add(tmp);
    //day month year
    tmp=check(1900+c,b,a);
    if(tmp!=0&&list.contains(tmp)==false) list.add(tmp);
    tmp=check(2000+c,b,a);
    if(tmp!=0&&list.contains(tmp)==false) list.add(tmp);
    Collections.sort(list);
    for(int i=0;i<list.size();i++){
        System.out.printf("%d-%02d-%02d\n", list.get(i) / 10000,  list.get(i)/ 100 % 100, list.get(i) % 100);
    }
        }
        
        public static int check(int year,int month,int day){
            if(year<1960||year>2059||month<=0||month>12||day<=0||day>31) return 0;//不符常理
            if(month==2){//2月 平年闰年之分
                if(((year%4==0)&&(year%100!=0))||(year%400==0)){//四年一闰 百年不闰;四百年再闰
                    if(day>29) return 0;
                }//闰年最多不超过29
                else if(day>28) return 0;//平年最多不超过28
            }
            else{//其他月份,按照月份表来
                if(day>Months[month-1]) return 0;
            }
            return year * 10000 + month * 100 + day;
        }
    }
    

航班时间

  • 题目

  • 解题关键:

    • 从A到B,加上时差;从B到A,减去时差;两式相加,时差可以被消掉
    • 将时间都统一转化为秒算更方便
  • 题解:

    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    
    public class Main{
        static BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
        public static void main(String[] args) throws IOException {
            int n=Integer.parseInt(br.readLine());//如果用read()的话,之后会读取缓冲区的空格而不是下一行的字符串
    while(n--!=0){
        int t=((get_time()+get_time())/2);
        System.out.printf("%02d:%02d:%02d",t/3600,t%3600/60,t%60);
        System.out.println();
    }
        }
    
        private static int get_time() throws IOException{
            String [] s=br.readLine().split(" ");//将同一行划分为出发、到达、(附加)
            String [] start=s[0].split(":");
            String [] end=s[1].split(":");
            int extra=0;
            if(s.length>2) extra=(Integer.parseInt(s[2].substring(2,3)))*24*3600;
            int h1=(Integer.parseInt(start[0]))*3600;
            int m1=(Integer.parseInt(start[1]))*60;
            int s1=Integer.parseInt(start[2]);
            int h2=(Integer.parseInt(end[0]))*3600;
            int m2=(Integer.parseInt(end[1]))*60;
            int s2=Integer.parseInt(end[2]);
    return (h2+m2+s2)-(h1+m1+s1)+extra;
        }
    }
    
  • 做题的血泪教训:

    • 运算符优先级问题:将字符串转换为整数并且乘上一些值的时候,如果不给Integer.parseInt()加个括号会导致精度丢失,最终输出结果的秒数不正确。

    • 一开始是数据的读取让我倍感抓狂。这道题给了我一个了解java.io中的BufferedReader类的契机。

      BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
      
      • BufferedReader接收的是字符流,而System.in提供的是字节流, InputStreamReader的作用就是将字节流转换为字符流。

      • BufferedReader的处理效率比Scanner类更高,读取数据越多Buffer的优势越明显。

      • BufferedReader:read() //读取单个字符 readline() //读取整行

        Scanner: (nextInt()、nextDouble()等等) next() //读取字符串 nextLine() //读取整行

        个人感觉:读取少数字符,特别是还细分数据类型的时候,Scanner类就够用了

        但是Scanner有个很大的问题就是默认将空格、回车之类的作为结束符,这对于数据当中有这些内容的读取不是很友好。

      当然,用Scanner也不是不行,下面再附上用Scanner的做法。

//Scanner版
import java.util.*;
public class Main{
    static Scanner sc=new Scanner(System.in);
    public static void main(String[] args){
        int n=Integer.parseInt(sc.nextLine());
while(n--!=0){
    int t=((get_time()+get_time())/2);
    System.out.printf("%02d:%02d:%02d",t/3600,t%3600/60,t%60);
    System.out.println();
}
    }

    private static int get_time(){
        String [] s=sc.nextLine().split(" ");//将同一行划分为出发、到达、(附加)
        String [] start=s[0].split(":");
        String [] end=s[1].split(":");
        int extra=0;
        if(s.length>2) extra=(Integer.parseInt(s[2].substring(2,3)))*24*3600;
        int h1=(Integer.parseInt(start[0]))*3600;
        int m1=(Integer.parseInt(start[1]))*60;
        int s1=Integer.parseInt(start[2]);
        int h2=(Integer.parseInt(end[0]))*3600;
        int m2=(Integer.parseInt(end[1]))*60;
        int s2=Integer.parseInt(end[2]);
return (h2+m2+s2)-(h1+m1+s1)+extra;
    }

}

无论用哪一个版本都要注意:对于开头的单个数字的读取,不能用读单个字符的方式,而应该读整行,然后将其转换成整数。因为这个读取操作是在缓冲区中进行,第一次只读取到回车之前的内容,导致第二次读取(字符串数据的第一行)变成了读取回车,导致读取失败

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值