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