1.幸运数字
*题目
package com.lanqiao;
import java.*;
public class test1 {
public static boolean check(int x,int base) {
int n=x,sum=0;
while(n!=0) {
sum+=n%base;
n/=base;
}
return x%sum==0;
}
public static void main(String[] args) {
int count=0;
int i;
for(i=1;;i++) {
if(check(i, 2)&&check(i, 8)&&check(i, 10)&&check(i, 16)) {
count++;
}
if(count==2023) {
System.out.println(i);
break;
}
}
}
}
1.1break和continue的区别
- break
(1)break是完全结束一个循环
(2)用在循环体内和switch语句内
注意:用在switch语句内只表示跳出该switch语句体,并不是终止整个循环 - continue
只是跳出本次循环,接着开始下一次循环
2.求和
*题目
求1(含)至20230408(含)中每个数的和.
package com.lanqiao;
import java.util.*;
public class test5{
public static void main(String[] args) {
// 循环求和
long sum1=0;
for(long i=1;i<=20230408;i++) {
sum1+=i;
}
System.out.println(sum1);
// 利用等差数列求和公式
long sum2=0;
long n=20230408;
sum2=((1+n)*n)/2;
System.out.println(sum2);
}
}
3.阶乘求和
*题目
3.1求阶乘
public static long mul(int x) {
if(x==0||x==1) {
return 1; //0和1的阶乘都为1
}else {
long mul=1;
for(int i=2;i<=x;i++) {
mul*=i;
}
return mul;
}
}
- 求和
- 我们发现,如果把1~202320232023的阶乘和都算出来,数太大了
- 经过观察发现,当阶乘的底数大于等于5时,阶乘结果的末尾开始出现0(是因为有因子2,5);
- 题目要求得出后九位,所以找出末尾9位都为0的阶乘结果(从这个数开始,后九位就都为0了,不会对最后结果有影响);
- 也就是要找到第十个因子5–>(5,10,15,20,25(2个5),30,35,40)---->所以只需要计算1~40的阶乘和,取后九位即可
- 所以,本题的代码应为
//因为40的阶乘已经达到了40+的位数—>代码中只计算后九位
public static void main(String[] args) {
int MOD=1000000000;
long mul=1L;//mul为i的阶乘
long sum=0L;//sum为各阶乘和
for(int i=1;i<=40;i++) {
mul=mul*i%MOD;
sum=(sum+mul)%MOD;
}
System.out.println(sum);
}
4.特殊日期
*题目
4.1权限使用区别(private,public,protected,default)
范围/权限 | private | default | protected | public |
---|---|---|---|---|
同一包中同一类 | √ | √ | √ | √ |
同一包中不同类 | √ | √ | √ | |
不同包中的子类 | √ | √ | ||
不同包中的非子类 | √ |
- 有关日期的计算—>要判断是否为闰年(闰年有2月29日)
//能被4整除且不能被100整除;或者能被400整除
// 判断闰年
static boolean is_leap(int y) {
return y%4 == 0 && y%100 != 0||y%400 == 0;
}
- 本题代码
package com.lanqiao;
public class test4 {
// 定义每月的天数
static int days[] = {0,31,28,31,30,31,30,31,31,30,31,30,31};
// 判断闰年(闰年返回true)
static boolean is_leap(int y) {
return y%4 == 0 && y%100 != 0||y%400 == 0;
}
// 如果是闰年,那么2月天数为29
static int daysofMonth(int y,int m) {
if(m==2&&is_leap(y)) {
return 29;
}
return days[m];
}
// 判断年=月+日
static boolean check(int y,int m,int d) {
int sum1=0;
while(y!=0) {
sum1+=y%10;
y/=10;
}
int sum2=0;
while(m!=0) {
sum2+=m%10;
m/=10;
}
while(d!=0) {
sum2+=d%10;
d/=10;
}
return sum1==sum2;
}
public static void main(String[] args) {
int count=0;
for(int i=1900;i<=9999;i++) {
for(int j=1;j<=12;j++) {
for(int k=1;k<=daysofMonth(i, j);k++) {
if(check(i, j, k)) {
count++;
}
}
}
}
System.out.println(count);
}
}
5.平均
*题目
import java.util.*;
import java.io.*;
// 1:无需package
// 2: 类名必须Main, 不可修改
public class Main{
static int n;
static ArrayList<Integer>[] e = new ArrayList[10];
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
n = Integer.parseInt(br.readLine());
for (int i = 0; i < 10; i++) e[i] = new ArrayList<>();
for (int i = 0; i < n; i++) {
String[] tokens = br.readLine().split(" ");
int a = Integer.parseInt(tokens[0]);
int v = Integer.parseInt(tokens[1]);
e[a].add(v);
}
long ans = 0;
for (int i = 0; i <= 9; i++) {
if (e[i].size() > n / 10) {
Collections.sort(e[i], Collections.reverseOrder());
for (int j = n / 10; j < e[i].size(); j++) {
ans += e[i].get(j);
}
}
}
System.out.println(ans);
}
}
5.1ArrayList
ArrayList与普通数组的最大区别就是它可以自动调节大小
ArrayList是java中提供的一个动态数组类,它实现了List接口
ArrayList可以根据需要自动调整大小,并且可以存储任意类型的对象
ArrayList的泛化类型只能为引用数据类型
下面是一些原语ArrayList的重要特点和常用方法:
1.动态大小:ArrayList的大小可以根据需要自动调整.他会自动增长或缩小以适应添加或删除元素
2.随机访问:ArrayList中的元素可以通过索引进行快速访问.可以所使用的get(index)方法获取指定索引位置的元素
3.允许重复元素:ArrayList可以存储重复的元素
4.可以存储任意类型的对象:ArrayList是一个泛型类,可以存储任意类型的对象,例如:ArrayList<String>可以存储字符串对象
下面是一些常用的ArrayList方法:
方法 | 解释 |
---|---|
add(element) | 将指定的元素添加到ArrayList的末尾 |
add(index,element) | 在指定的索引位置插入指定的元素 |
get(index) | 获取指定索引位置的元素 |
set(index,element) | 将指定索引位置的元素替换为新的元素 |
remove(index) | 删除指定索引位置的元素 |
size() | 返回ArrayList中的元素个数 |
isEmpty() | 检查ArrayList是否为空 |
contains(element) | 检查ArrayList是否包含指定的元素 |
clear() | 清空ArrayList中的所有元素 |
5.2引用数据类型
5.3BufferedReader
5.3.1字节流和字符流
-
流的分类
按照流向 : 输入流(如:键盘,麦克风);输出流(如:显示器,音响)
按传输单位 : 字节流;字符流 -
字节流 : 以字节为单位进行读写,适用于处理二进制数据(如图像,音频,视频等)或文本数据的原始字节.
-
字符流 : 以字符为单位进行读写,适用于处理文本数据
字节流 - 字节流以字节为单位进行读写,主要用于处理为禁止数据或文本数据的原始字节 - 字节流类位于'java.io'包中,例如'InputStream'和'OutputStream' - 字节流类提供了读取和写入字节的方法,如'read()'和'write()' - 字节流适用于处理图像,音频,视频等二进制数据,以及处理文本数据时不需要进行字符集编码转换的情况 字符流 - 字符流以字符为单位进行读写,主要用于处理文本数据 - 字符流类位于'java.io'包内,例如'Reader'和'Writer' - 字符流类提供了读取和写入字符的方法,如'read()'和'writer()' - 字符流在读取和写入文本数据时会自动进行字符集编码和解码,是的处理数据更加方便灵活 - 字符流适用于处理文本文件,配置文件,文档等文本数据
5.3.2而BufferedReader是java的输入流类之一
-
主要用于从字符输入流中读取文本,并将文本缓存到缓冲区中,从而提高读取文本的效率
-
BufferedReader只能用来读取字符类型的数据,如果需要读取其他类型的数据,需要进行数据转换
应用场景: BufferedReader类适用于需要高效读取字符输入流的场景,比如从文件中逐行读取文本内容,读取网络数据流等
5.4split
split()是Java中’String’类中的一个方法,用于将字符串拆分成多个子字符串,根据指定的分隔符进行拆分.
split(String regex)方法
- 1.单个分割符
String str="1 2 3";
String[] data=str.split(" ");
for(int i=0;i<data.length;i++){
System.out.println(data[i]);
}
//输出结果:
1
2
3
- 2.如果分隔符为"|“,那么需要使用转义字符”"让其产生效果
String str="a|bc|d";
//java中 \\ 表示一个普通的 \ ;\+特殊字符表示字符本身
String[] data=str.split("\\|");
for(...){
sout(data[i]);
}
//输出结果为
a
bc
d
- 3.如果直接使用 |
String str="a|bc|d";
String[] data=str.split("|");
for(...){
sout(data[i]);
}
//输出结果为:
a
|
b
c
|
d
- 4.多个分隔符
String str="a;1,2,3";
String[] data=str.split(";|,");
for(...){
sout(data[i]);
}
//输出结果为:
a
1
2
3
5.5Collections
- Collections不能new对象,因为Collections的构造方法被私有化处理了
- 可以通过 类名.方法名 直接调用Collections中的方法(因为Collections中的方法都是static方法)
5.5.1Collections.sort()
- 默认是升序排列
- 也可以存储对象类型的数据---->要让自定义的类实现Comparabler接口,并重写Comparabler接口中的compare to方法
//自定义的类
public class Person implements Comparable<Person>{
private String name;
private int age;
...
//重写CompareTo排序的规则
@Override
public int compareTo(Person o){
//自定义比较的规则 this指代当前对象,o指代后一个要传进来的对象
return this.getAge()-o.getAge();//按照年龄升序排序
//return -(this.getAge()-o.getAge());//按照年龄的降序排序
//main
public static void main(String[] args){
ArrayList<Person> list=new ArrayList<>();
list.add(new Person("张三",18));
list.add(new Person("李四",20));
list.add(new Person("王五",15));
System.out.println(list);
Collections.sort(list);
System.out.println(list);
//输出结果:
王五,15
张三,18
李四,20
compareTo的逻辑
-
- compareTo()方法知识sort排序中的很少一部分代码---->我们把这部分代码单拎出来重写---->来实现我们的目的
-
- 重写@Override compareTo的目的---->在自定义的类中,定义自己的排序规则(不然机器是不知道该如何排序的)---->可以在使用sort()方法排序时,使用自己的排序规则进行排序
-
-
compareTo 的作用---->提供一个 i 的返回值
---->返回值为**正数**,则在sort其他部分的代码中会转换为 **1** ;然后sort的其他代码段,将会对参与比较的两个对象**对调次序**; ---->返回值为**负数**,则会转化为 **-1** ;在sort的其他代码段中,**不会**进行**对调** ---->如果参与比较的两个数相等,会返回 0 .
-
-
在上述代码中,return this.getAge()-o.getAge()
- 如果返回值是正数,则要与后一个参数进行对调---->也就是年龄小的排在前面---->升序排列 - 如果返回值是负数,则不进行操作--->还是年龄小的排在前面---->升序排列 - 所以this.getAge()-o.getAge()实现的是升序排列 - 反之,则可进行 降序排列
5.5.2 Collections.reverseOrder()
是Collections中的一个静态方法—>功能是 : 反转默认的自然排序顺序
例子:
import java.util.ArrayList;
import java.util.Collections;
public class ReverseOrderExample {
public static void main(String[] args) {
ArrayList<Integer> numbers = new ArrayList<>();
numbers.add(5);
numbers.add(2);
numbers.add(8);
numbers.add(1);
numbers.add(10);
Collections.sort(numbers, Collections.reverseOrder());
System.out.println(numbers);
}
}
输出结果:
[10, 8, 5, 2, 1]
其他请看:
6.棋盘
*题目
package com.lanqiao;
import java.util.Scanner;
public class test6_1 {
public static void main(String[] args) {
Scanner scanner=new Scanner(System.in);
// 输入n,m
int n=scanner.nextInt();
int m=scanner.nextInt();
// 创建一个数组,用于存储(x1,y1);(x2,y2)
int[][] b=new int[n+2][n+2];
// 输入x1,y1,x2,y2--->要输入m次
for(int i=1;i<=m;i++) {
int x1=scanner.nextInt();
int y1=scanner.nextInt();
int x2=scanner.nextInt();
int y2=scanner.nextInt();
// 根据每次输入的x,y 将对应区域的格子数值+1
b[x1][y1]++;
b[x2+1][y1]--;
b[x1][y2+1]--;
b[x2+1][y2+1]++;
}
// 计算m次操作后的数组,(每个格子最后的数值为偶数-->白色;奇数-->黑色)
for(int i=1;i<=n;i++) {
for(int j=1;j<=n;j++) {
// 每个格子的数值
b[i][j]=b[i][j]+b[i-1][j]+b[i][j-1]-b[i-1][j-1];
// 输出n行 ; 如果是白色则输出0,如果是黑色则输出1
if (b[i][j]%2==0) {
System.out.print("0");
}else {
System.out.print("1");
}
}
System.out.println();
}
scanner.close();
}
}
- 二维数组定义的 n+2
6.1数组
6.1.1一维数组
数组是一组相同类型元素的集合
- 数组的创建方式:
type name [n]
数组的元素类型 数组的名字 数组的大小
eg.int arr[10]
- 数组的初始化:
eg.int arr[3]={1,2,3}
(可以不定义数组的大小)
int arr[]={1,2,3,4,5,6}
6.1.2二维数组
- 二维数组创建时,行数可以忽略不写(所有维度的数组,第一个方括号的内容可以忽略)
- 二维数组可以用于表示矩阵,图像,游戏地图等具有二维结构的数据.
- 通过使用嵌套的循环,可以对二维数组进行遍历和操作
int h[3][4];
h[0][0] | h[0][1] | h[0][2] | h[0][3] |
---|---|---|---|
h[1][0] | h[1][1] | h[1][2] | h[1][3] |
h[2][0] | h[2][1] | h[2][2] | h[2][3] |
二维数组的创建,初始化,遍历输出
int[][] matrix = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix[i].length; j++) {
System.out.print(matrix[i][j] + " ");
}
System.out.println();
}
6.2差分
6.2.1什么是差分?
- 首先有一个原数组a: a[1],a[2],a[3]…a[n]
- 然后构造一个数组b: b[1],b[2],b[3]…b[n],使得a[n]=b[1]+b[2]+b[3]+…+b[n]
- 所以,我们就称a数组是b数组的前缀和,而b数组就称为a数组的差分
- 差分数组:每个位置的值表示原始数组对应位置的 增量
6.2.2一维差分
- 就是给区间[l,r]中的每个数加上c--->b[l]+=c , b[r+1]-=c , 并且时间复杂度为O(1)
注意 : 只需要在b[L]的位置加一个c---->整个a[L]数组,每个a[i]都会加c
计算前缀和 a[n]=a[n-1]+b[n]
6.3二维差分
计算前缀和(根据差分计算出最终结果)
- 普通版:时间复杂复为O(n*n)
for(int i=x1;i<=x2;i++){
for(int j=y1;j<=y2;j++){
a[i][j]+=c;
}
}
- 差分矩阵核心操作:时间复杂度为O(1)
static void insert(int x1,int y1,int x2,int y2,int c){
b[x1][y1]+=c;
b[x1][y2+1]-=c;
b[x2+1][y1]-=c;
b[x2+1][y2+1]+=c;
}
- 计算前缀和
n行,m列的数组
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
s[i][j]=s[i-1][j]+s[i][j-1]+s[i][j]-s[i-1][j-1];
}
}
7.与或异或
*题目
package com.lanqiao;
public class test7_1 {
static int sum=0;
static int[] ops=new int[11];//本题共10个操作符
static int[][] op=new int[5][5];//对上面操作符的二维化
static int[][] arr=new int[6][6];//表示对应的1/0
public static void dfs(int cnt) {
// cnt==11时,代表10个操作符已经赋值完了---->形成了一种方案
if(cnt==11) {
for(int i=1;i<=4;i++) op[1][i]=ops[i];
for(int i=1;i<=3;i++) op[2][i]=ops[i+4];
for(int i=1;i<=2;i++) op[3][i]=ops[i+7];
for(int i=1;i<=1;i++) op[4][i]=ops[i+9];
// 为了方便--->用0,1,2表示三种操作符
for(int i=1;i<=4;i++) {
for(int j=1;j<=4-i+1;j++) {
if(op[i][j]==0) arr[i][j]=arr[i-1][j] | arr[i-1][j+1];
if(op[i][j]==1) arr[i][j]=arr[i-1][j] ^ arr[i-1][j+1];
if(op[i][j]==2) arr[i][j]=arr[i-1][j] & arr[i-1][j+1];
}
}
if(arr[4][1]==1)
sum++;
return;
}
for(int i=0;i<3;i++) {
// 枚举第cnt个操作符为i
ops[cnt]=i;
// 开始枚举下一个操作符
dfs(cnt+1);
}
}
public static void main(String[] args) {
arr[0][1]=1;
arr[0][2]=0;
arr[0][3]=1;
arr[0][4]=0;
arr[0][5]=1;
dfs(1);
System.out.println(sum);
}
}
逻辑门
1.1与门---->且 , A&&B
- 全真才为真
输入 A | 输入 B | 输出 Q |
---|---|---|
1 | 1 | 1 |
1 | 0 | 0 |
0 | 1 | 0 |
0 | 0 | 0 |
1.2或门---->或 , A||B
- 有真就为真
输入 A | 输入 B | 输出 Q |
---|---|---|
1 | 1 | 1 |
1 | 0 | 1 |
0 | 1 | 1 |
0 | 0 | 0 |
1.3非门---->非 , !A
- 取反
输入 A | 输出 Q |
---|---|
1 | 0 |
0 | 1 |
1.4与非门---->!(A&&B)
与门+非门
1.5或非门---->!(A||B)
或门+非门
1.6异或门---->A^B
- 输入不同–>输出1
- 输入相同–>输出0
1.7同或门---->!(A^B)
异或门+非门
- 输入不同–>输出0
- 输出相同–>输出1
8.填充
*题目
package com.lanqiao;
import java.util.Scanner;
public class test8_1 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String s = scanner.next();
int n=s.length();
s="?"+s;
int[] f=new int[n+1];
for(int i=2;i<=n;i++) {
if((s.charAt(i) == s.charAt(i-1)) || s.charAt(i)=='?' || s.charAt(i-1)=='?') {
f[i]=f[i-2]+1;
}else {
f[i]=f[i-1];
}
}
System.out.println(f[n]);
}
}
8.1charAt()方法
9.数组分割
package com.lanqiao;
import java.util.Arrays;
import java.util.Scanner;
public class test10_1 {
// 根据题目定义的两个常量 : N数组大小
static final int N=1010,MOD=(int) 1e9+7;
static int[] a=new int[N];
// dp[i][j]数组表示 前i个元素的和的奇偶 --->如果为偶dp[i][0] ; 如果为奇--->dp[i][1]
//dp[i][j]数组的值表示成立的方案个数
static int[][] dp=new int[N][2];
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int T=scanner.nextInt();
// 循环输入T组数据
for(int k=1;k<=T;k++) {
// 输入数组个数
int n=scanner.nextInt();
long sum=0;
// 输入数组的值 ; 并求数组的整和
for(int i=0;i<n;i++) {
a[i]=scanner.nextInt();
sum+=a[i];
}
// 如果和为奇数-->那一定从不成两个都是偶数的S(因为 奇数+?=奇数)
if(sum%2==1) {
System.out.println(0);
continue;
}
// 初始化 动态规划的数组dp[i][j] 都为0
for(int i=0;i<=n;i++) {
Arrays.fill(dp[i], 0);
}
// 题目允许为空--->前0个数组的和为0-->偶数-->所以dp[0][0]=1
dp[0][0]=1;
for(int i=1;i<=n;i++) {
if(a[i]%2==1) {
dp[i][0]=(dp[i-1][0]+dp[i-1][1])%MOD;
dp[i][1]=(dp[i-1][0]+dp[i-1][1])%MOD;
}else {
dp[i][0]=dp[i-1][0]*2%MOD;
dp[i][1]=dp[i-1][1]*2%MOD;
}
}
System.out.println(dp[n][0]);
}
}
}
10.买二赠一
*问题
package com.lanqiao;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;
public class test12_1 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
long sum=0;
int n=scanner.nextInt();
int[] a=new int[n];
for(int i=0;i<a.length;i++) {
a[i]=scanner.nextInt();
}
Arrays.sort(a);
//A 队列:当前未购买的所有商品
Queue<Integer> A=new LinkedList<Integer>();
//Free 队列 存放 可以免费的商品
Queue<Integer> Free=new LinkedList<Integer>();
for(int i=a.length-1;i>=0;i--) {
A.add(a[i]);
}
int count=1;
while(!A.isEmpty()) {
long x=A.poll();
// 满足免费条件的-->直接跳出本次循环 , 并将免费队列的头元素删除(因为 二换一)
if(!Free.isEmpty() && x<=Free.peek()) {
Free.poll();
continue;
}
// 不能免费的,直接购买
sum+=x;
if(count==2) {
Free.add((int) (x/2));
count=1;
}else {
count=2;
}
}
System.out.println(sum);
}
}
- 队列