文章目录
前言
昨天随便瞄了一眼去年的Java B组的题目,一来就先搞出4题,当然不保证是最优解。
今天主要还是以阐述思路为主,适当优化别人的代码,毕竟这种题解网上都不少,我只是再把具体的思路复述一遍,力求能够摸清楚一点套路。
我们按照题目难度来说名。
首先是昨天的4题,不用想,这个妥妥送分。
ASC
public class ASC {
public static void main(String[] args) {
System.out.println((int) 'L');
}
}
卡片
public class 卡片 {
//由题意知道1的消耗是最大的,所以没有必要去把0-9的那个去统计
static int count = 2021;
public static void main(String[] args) {
for (int i = 1; i <=10000; i++) {
String temp = String.valueOf(i);
if(temp.contains("1")){
for (char c : temp.toCharArray()) {
if(c=='1'){
count--;
}
}
}
if(count==0){
System.out.println(temp);
break;
}else if (count<0){
System.out.println(i-1);
break;
}
}
}
}
时间限制
class Time{
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
long time = scanner.nextLong();
time /=1000;
int a= (int) (time % (24 * 3600));
int h = a / 3600;
a %= 3600;
int m = a/60;
int s = a%60;
System.out.printf("%02d:%02d:%02d\n",h,m,s);
}
}
这里值得一提的是 java 其实还提供了printf()这个函数,所以我们不需要输出的那么辛苦。
杨辉三角
昨天太晚了,没注意,给的代码是原来没调好的。
public class 杨辉三角 {
static int[] yanghui;//搞大一点
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
if(n==1){
System.out.println(n);
}
yanghui = new int[1000000];//搞一点,理论上可以先算一个阈值 n*n/2 <=10000000
yanghui[0]=1;
yanghui[1]=1;
yanghui[2]=1;
int idx = 2; //当前第第几个
for (int i=3;;i++){
//卡阈值死磕,来骗~,现在是从第三层开始
idx++;
yanghui[idx]=1;
int j=1;
for (;j<i-1;j++){
idx++;
yanghui[idx]=yanghui[idx-(i)]+yanghui[idx+1-i];
//条件判断
if(yanghui[idx]==n){
System.out.println(idx+1);
return;
}
}
idx++;
yanghui[idx]=1;
}
}
}
怎么说呢,肯定不是最优解,不过骗到是应该能够骗点分数。
货物摆放
说句实话,我对这种题目不是很敏感,意思其实很好理解,说白了就是 一个数组,你去组合成三个数字,然后相乘等于自己本身。
我当时的第一想法肯定是暴力,暴力枚举 但是 n的三次幂挺大的。所以我就在想这些数有没有什么规律啥的。然后,什么数字相乘能够得到自己本身,那么不就是可以被自己整除的数子嘛,也就是约数呀。所以代码出来了。
求取约数
首先我们要做的是求约数,这里的话是有一个公式的。
首先是最简单的
for(int i=1;i<=Math.sqrt(n);i++){
if(n%i==0){
res.add(i);
res.add(n/i);}
}
}
这个的时间复杂度是O log n
本来是这样的,但是由于你的 i 都是小于人家方根的,所以后面那个if一定成立,减少了很多不必要的运算。
for(int i=1;i<Math.sqrt(n);i++){
if(n%i==0){
res.add(i);
}
if(i*i!=n){
res.add(n/i);
}
}
public class 货物摆放 {
static int ans = 0;
static long num = 2021041820210418L;
public static void main(String[] args) {
long end = (long)Math.sqrt(num);
Set<Long> div = new HashSet<>();
for (long i = 1; i <= end; i++) {
if (num % i == 0) {
div.add(num/i);
div.add(i);
}
}
Long[] arr = div.toArray(new Long[0]);
for (long i : arr) {
for (long j : arr) {
for (long k : arr) {
if (i * j * k == num) {
ans++;
}
}
}
}
System.out.println(ans);
}
}
直线
这两个填空题的话,怎么说呢,难度上其实都应该差不多,不过这个直线的创新性稍微强一点。
这里的想法也很简单,没错就是生成那个矩阵,然后去枚举,重点是去重,这里去重的话,直接考虑使用Set。反正都是暴力。
我们这边就先考虑 有斜线的情况,不考虑连接成与坐标轴平行的线。
后面再加回去嘛,主要是公式这边不好处理。
public class 直线 {
public static void main(String[] args) {
Set<Map<Double,Double>> linear = new HashSet<>();
List<Map<Integer,Integer>> points = new ArrayList<>();
//初始化所有点
for (int i=0;i<20;i++){
for(int j=0;j<21;j++){
HashMap<Integer, Integer> temp = new HashMap<>();
temp.put(i,j);
points.add(temp);
}
}
//遍历计算点
for (int i=0;i<points.size();i++){
for(int j=i+1;j<points.size();j++){
double x1=0;double y1=0;
double x2=0;double y2=0;
//解包
for (Map.Entry<Integer, Integer> point1 : points.get(i).entrySet()) {
x1=point1.getKey();
y1=point1.getValue();
}
for (Map.Entry<Integer, Integer> point1 : points.get(j).entrySet()) {
x2=point1.getKey();
y2=point1.getValue();
}
if(x1==x2||y1==y2){
continue;
//水平的情况,先不要
}
//计算斜率
double k=(y2-y1)/(x2-x1);
double b=(x2*y1 -x1*y2)/(x2-x1);
HashMap<Double, Double> temp = new HashMap<>();
temp.put(k,b);
//对于这种数据类型,equal方法是按照值去对比的
linear.add(temp);
}
}
System.out.println(linear.size()+20+21);
}
}
路径
这个题目本身不是很难,主要是构建那个图稍微麻烦一点。
构建好之后,咱们再去找最短路径。
这里有两个算法一个是 弗洛伊德,还有一个是 迪杰特斯 的方法。
最简单的当然是第一个。
public class Main {
static int[][] graph = new int[2050][2050];
static final int INF = 0x3f3f3f3f;
private static void floyd() {
for (int k = 1; k <= 2021; k++) {
for (int i = 1; i <= 2021; i++) {
for (int j = 1; j <= 2021; j++) {
if (i != j && graph[i][j] > graph[i][k] + graph[k][j]) {
graph[i][j] = graph[i][k] + graph[k][j];
}
}
}
}
}
private static int gcd(int a, int b) {
return b == 0 ? a : gcd(b, a % b);
}
public static void main(String[] args) {
for (int i = 1; i <= 2021; i++) {
for (int j = 1; j <= 2021; j++) {
graph[i][j] = INF;
}
}
for (int i = 1; i <= 2021; i++) {
int st = Math.max(i - 21, 1);
for (int j = st; j <= i; j++) {
int div = gcd(j, i);
int lcm = i * j / div;
graph[i][j] = lcm;
graph[j][i] = lcm;
}
}
floyd();
System.out.println(graph[1][2021]); // 10266837
}
}
这里简单说一下原理。其实很简单
看那个大循环就知道了,A–》B假设走A中转看距离,然后走完后,在假设走B中转,知道假设玩全部。
例如A–》D 第2次遍历假设到走B近,那么此时更新到B中转,然后再问遍历的C中转,发现在原来的基础上还更近,那么此时再加上走C的距离,此时你就得到了A–》D 中转 B C 近。
接的下来是后面三道大题冲刺。 按照刷题的经验,基本上第十题应该是最难的,第一题应该是简单的,其他的不好说,所以一定要先全部预览一下题目。包括,昨天,我就是9,10题基本上没看,以为很难。结果第九题嘿嘿,还是能骗分的。
到这里,已经“大力飞砖”了大概 7 题,其实发现,这些题目,比原来的数据量大,填空题几乎不可能手推出来,而且考到了比较多的数据结构方面的东西,对比原来的 13 14 包括20 和原来的模拟题怎么说呢,题目是越来越绕了。我这糟糕的语文能力。
双向队列
这里还要我说嘛,工具类,暴力来骗~
import java.io.BufferedInputStream;
import java.util.Arrays;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(new BufferedInputStream(System.in));
int n = in.nextInt(), m = in.nextInt();
Integer[] arr = new Integer[n + 1];
for (int i = 1; i <= n; i++) {
arr[i] = i;
}
for (int i = 0; i < m; i++) {
int p = in.nextInt();
int split = in.nextInt();
if (p == 0) {
Arrays.sort(arr, 1, split + 1, (a, b) -> Integer.compare(b, a));
} else {
Arrays.sort(arr, split, n + 1);
}
}
for (int i = 1; i <= n; i++) {
if (i > 1) {
System.out.print(" ");
}
System.out.print(arr[i]);
}
in.close();
}
}
这里有一个输入优化,请务必记住
new Scanner(new BufferedInputStream(System.in))
到此,好骗的大题目估计就骗到了~
括号匹配
为什么先说在这个咧,原因很简单,dp,我想不出来,我只能想到暴力!
public class 括号匹配 {
static int count = 0;
static int end = 0;
public static void main(String[] args) {
Scanner scanner = new Scanner(new BufferedInputStream(System.in));
String next = scanner.next();
for (char c : next.toCharArray()) {
if(c=='(') end++;
}
f(0,0);
System.out.println(count%100000007);
}
public static void f(int left,int right){
//我们是从0开始放,放到end下标的时候,说明我们前面的都放完了
if(left==end){
count++;
count%=100000007;
return;
}
f(left+1,right);
if(left>right){
f(left,right+1);
}
}
}
不过这个骗不了几个分。
最少砝码
这道题目是第七题你敢信,反正第一眼我没啥思路,模拟暴力,也不对。
然后我看了题解,陷入了沉默,规律很好找,只是我找到方向错了。
这个题目怎么说呢,还是假设嘛
还是一个一个推,从一开始
选1、2的砝码可以满足1=1,2=2,1+2=3
选1、3的砝码可以满足1=1,2=3-1,3=3,4=3+1
也就是这样想
假设质量1 要称出来 只能有1
假设质量2 要出来 只能有 1 2
假设质量3 要出来 1 2 就够了
假设质量4 要出来 1 3 就够了
此时 1 3 可以表示 1 2 3 4
对于7: 1 4 6 我可以表示从1到7,对于质量6我也能够表示
一直推下去
选1、3、9的组合可以满足小于等于13(13=1+3+9)的所有重量
然后打表
序号 | 砝码质量 | 最大表示质量 |
---|---|---|
1 | 1 | 1 |
2 | 3 | 4 |
3 | 9 | 13 |
4 | 27 | 40 |
此时规律不就出来了,我只要看你的那个值的范围,然后生成这个表格,不仅我可以知道你的那个长度
我还能知道你是什么砝码组成。
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
sc.close();
int weight = 1;
int count = 1;
int total = 1;
while (total < n) {
count++;
weight *= 3;
total += weight;
}
System.out.println(count);
}
}
快捷键
之后是关于ecplise的快捷键,这里你只需要记住三个。
和一个设置。
说句实话,搞不懂,那么费劲的玩意怎么还活着,IDEA比他牛,收费,这个没办法,但是我VSCODE 不是吊打它嘛!要不是比赛,真不想用那破玩意。界面难看用起来还膈应,你说装插件,我靠我直接用vscode不比他香,java 8 不支持了,用老版本能咋地,说的你ecplise 新版本就支持java 8一样。
第一个是关于代码提示设置。这个很简单。
点击一下第二个
快捷键
一个是 crtl + 1 代码修复
一个是 Ctrl+Shift+O 自动引导类包,不过,第一个可以有这个功能
还有一个是 alt + shift + s 作用和 idea 的 alt + ins 一样
其他的没必要记,至于代码调试,自己打断点,快捷键和idea差不多,不行就用 那个图形化的提示点点嘛,实在不行自己输出结果自己看。
总结
关于一些,什么常见的数学公式啥的,我记得也少。
这里再水边补充一下。
求约数
for(int i=1;i<=Math.sqrt(n);i++){
if(n%i==0){
res.add(i);
res.add(n/i);}
}
}
快速幂乘
int res = 1;
int a 你要求的数,n是幂数
while(n>0){
if(n%2!=0){
res*=a;
n=n-1;
}
n/=2;
a*=a;
}
快速幂取余
int res = 1;
int a 你要求的数,n是幂数
int m 是余数
while(n>0){
if(n%2!=0){
res*=a%m;
n=n-1;
}
n/=2;
a*=a%m;
}
a = a%m;
这个是公式退出来的。
由于是java 的话 int 会得到整数 5/2 和 4/2效果一样,所以可以省略 n-1 但是如果你是Python 的话不行。
欧拉取公约数
public static int gcd(int m,int n){
return n==0?m:gcd(n,m%n);
}
最小公倍数
这个就是 m*n / gcd(m,n)
分解质因数
public static String res(int num){
StringBuffer sb = new StringBuffer(num + "=");
int i=2;
while(i<=num){
if(num%i==0{
sb.append(i).append("*");
num=num/i;
i=2
}else{
i++;
}
}
return sb.toString().substring(0, sb.toString().length() - 1);
}
最后祝我好运吧!谢谢