calendar
主要是给出了年月日后,可以求出这一天是周几,如2013年的javaB组蓝桥杯第一题世纪末的星期,或者2020第二题纪念日
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
public class t {
public static void main(String[] args) throws ParseException {
Calendar calendar = Calendar.getInstance();// 初始化
calendar.set(2022, 2, 26);// 设置年月日 注意月份要比实际少一,他是从0开始计数的
System.out.println(calendar.get(Calendar.DAY_OF_WEEK));// 得出这一天是周几,注意7表示周六,1表示周日
calendar.add(Calendar.MONTH, 1);// 月份加一
System.out.println(calendar.getTime());
calendar.add(Calendar.DATE, 1);// 天数加一
System.out.println(calendar.getTime());
System.out.println("------------------");// Date设置calendar
SimpleDateFormat a = new SimpleDateFormat();// 设置date的格式
a.applyPattern("yyyy-MM-dd");
Date date = a.parse("2022-3-26");// 这里正常输入就行了
calendar.setTime(date);
System.out.println(calendar.get(Calendar.DAY_OF_YEAR));
System.out.println("--------------------------");
// 2020 年 7 月 1 日是中国共产党成立 99 周年纪念日。
// 中国共产党成立于 1921 年 7 月 23 日。
// 请问从 1921 年 7 月 23 日中午 12 时到 2020 年 7 月 1 日中午 12 时一共包
// 含多少分钟?
Calendar calendar1 = Calendar.getInstance();// 初始化
Calendar calendar2 = Calendar.getInstance();// 初始化
calendar1.set(1921, 6, 23, 12, 0);
calendar2.set(2020, 6, 1, 12, 0);
System.out.println((calendar2.getTimeInMillis() - calendar1.getTimeInMillis()) / 1000 / 60);
}
}
BigDecimal
和他相似的还有BigInter但是之所以写这个,是因为它涉及到精度问题,应用比较多,需要记忆一下。
就像2013年第4题的黄金连分数他要求四舍五入到小数点后100位,这就需要大分数了
● 初始化问题
尽量用字符串的方式初始化,否则会有精度问题。详看链接
● 除法精度问题
上面那个链接讲的已经很棒了。我在下面强调一下常用的方法
import java.math.BigDecimal;
public class 大分数 {
public static void main(String[] args) {
BigDecimal a = new BigDecimal("1");
BigDecimal b = new BigDecimal("3");
// System.out.println(a.divide(b));
System.out.println(a.divide(b, 3, BigDecimal.ROUND_HALF_UP));// 四舍五入方法保留三位小数
System.out.println(a.divide(b, 2, BigDecimal.ROUND_DOWN));
System.out.println("-------------------------------");
BigDecimal c = new BigDecimal("2.12345");
System.out.println(c.setScale(4, BigDecimal.ROUND_HALF_UP));
System.out.println(c.setScale(4, BigDecimal.ROUND_HALF_DOWN));
}
}
如果去掉上面的注释会报错,因为它的结果是个无限循环小数,没有规定舍入方式,无法给出结果,所以报错了。
Arrays.sort 和Collections.sort
常用的就是自定义类自定义排序方法
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.Vector;
public class 排序 {
static class node {
int x, y;
public node(int x, int y) {
// TODO Auto-generated constructor stub
this.x = x;
this.y = y;
}
}
static class Myconmpator implements Comparator {// 自定义排序
public int compare(node a, node b) {
if (a.x < b.x)
return -1;// 这样是升序排序???????????????
else if (a.x > b.x)
return 1;
else if (a.y < b.y) {
return -1;
} else if (a.y > b.y) {
return 1;
}
return 0;
}
}
public static void main(String[] args) {
Vector a = new Vector();
a.add(1);
a.add(4);
a.add(3);
a.add(5);
a.add(6);
Collections.sort(a);
for (Integer e : a) {
System.out.print(e + " ");
}
System.out.println();
System.out.println("----------------------");
node[] b = new node[5];
b[1] = new node(1, 2);
b[2] = new node(1, 3);
b[3] = new node(2, 2);
b[4] = new node(4, 2);
b[0] = new node(1, 4);
// Collections.sort(b);//Collections不能对自定义类排序,他只能对集合排序
// Arrays.sort(b);
Myconmpator cmp = new Myconmpator();
Arrays.sort(b, cmp);
for (int i = 0; i < b.length; i++) {
System.out.println(b[i].x + ":" + b[i].y + " ");
}
System.out.println("-------------------------------");
Map c = new HashMap();
c.put(1, 4);
c.put(1, 2);
c.put(1, 3);
c.put(2, 2);
c.put(3, 2);
// Collections.sort(c,cmp);//也不能对map进行排序
// Arrays.sort(a, Collections.reverseOrder());//集合不能用Arrays
Collections.sort(a, Collections.reverseOrder());// 不用自己定义逆序
for (Integer e : a) {
System.out.print(e + " ");
}
System.out.println();
System.out.println("----------------------");
Arrays.sort(b, Collections.reverseOrder(cmp));// 自定义类的逆序
for (int i = 0; i < b.length; i++) {
System.out.println(b[i].x + ":" + b[i].y + " ");
}
}
}
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.Vector;
public class 排序 {
static class node implements Comparable {
int x, y;
public node(int x, int y) {
// TODO Auto-generated constructor stub
this.x = x;
this.y = y;
}
public int compareTo(node b) { // 实现 Comparable 接口的抽象方法,定义排序规则
if (this.x < b.x)
return -1;// 这样是升序排序???????????????
else if (this.x > b.x)
return 1;
else if (this.y < b.y) {
return -1;
} else if (this.y > b.y) {
return 1;
}
return 0; // 升序排列,反之降序
}
}
static class Myconmpator implements Comparator {// 自定义排序
public int compare(node a, node b) {
if (a.x < b.x)
return -1;// 这样是升序排序???????????????
else if (a.x > b.x)
return 1;
else if (a.y < b.y) {
return -1;
} else if (a.y > b.y) {
return 1;
}
return 0;
}
}
public static void main(String[] args) {
System.out.println("-------TreeSet----------");
Set d = new TreeSet();// 这里要在类里面继承comparable并重写,否则报错
d.add(new node(1, 3));
d.add(new node(1, 2));
d.add(new node(2, 3));
for (node e : d) {
System.out.println(e.x + ":" + e.y);
}
System.out.println("------HashSet--------");
// Arrays.sort(d, cmp);//报错
// Collections.sort(d, cmp);//报错
Set f = new HashSet();
f.add(3);
f.add(2);
f.add(5);
f.add(1);
f.add(6);
for (Integer e : f) {
System.out.println(e + " ");
} // 我没有排序但是他是有序的,说明set也是自动排序的,不需要TreeSet
System.out.println("--------HashSet----------");
Set g = new HashSet();// 这里要在类里面继承comparable并重写,否则报错
g.add(new node(1, 3));
g.add(new node(1, 2));
g.add(new node(2, 3));
g.add(new node(3, 2));
for (node e : g) {
System.out.println(e.x + ":" + e.y);
} // 但是HashSet对node型没有排序
System.out.println("--------TreeMap---------");
Map h = new TreeMap();
h.put(2, 3);
h.put(2, 1);
h.put(2, 4);
h.put(5, 5);
h.put(3, 3);
for (Integer e : h.keySet()) {
System.out.print(e + " ");
}
System.out.println();
for (Integer e : h.values()) {
System.out.print(e + " ");
}
}
集合
常用的Map、List、Set、Vector
感觉List和Vector也没有什么太大区别
queue集合
集合的遍历问题
如果用迭代器遍历集合,无论对集合做增加还是删除操作都会发生错误
解决办法1:记录要改变的量,遍历完集合后再改变
解决办法2:用iterator删除
import java.util.Iterator;
import java.util.Vector;
public class 集合 {
public static void main(String[] args) {
Vector a = new Vector();
a.add(1);
a.add(4);
a.add(3);
a.add(5);
a.add(6);
Iterator it = a.iterator();
while (it.hasNext()) {
Integer e = it.next();
System.out.print(e + " ");
if (e == 3) {
it.remove();
}
}
it = a.iterator();// 这里要再次赋值,因为上一个it已经走到最后了
System.out.println();
while (it.hasNext()) {
Integer e = it.next();
System.out.print(e + " ");
}
}
}
集合的赋值问题
集合是引用类型,如果用等号赋值,那么他会直接变成两个不同的变量名指向同一个地址,但这并不是我的本意,这样造成一个变量变了,另一变量也会改变。奔溃的是求生成子集的一道题,用的集合,结果一直不对,后来发现不能用赋值号,要把一个集合的值给另一个集合应该直接添加!!!!!!!!!!!!!!!!代码
import java.util.HashSet;
import java.util.Scanner;
import java.util.Set;
public class tt {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
Set> set = new HashSet>();
Set all = new HashSet();
for (int i = 1; i < n + 1; i++) {
all.add(i);
Set temp = new HashSet();
temp.add(i);
set.add(temp);
}
int k = 1;
while ((n--) > 0) {
Set temp = new HashSet();
temp.addAll(all);// 一开始写了temp=all这样他们两个实际是一个地址,temp变了all也会变
Set> temp1 = new HashSet>();
for (Set e : set) {
temp.removeAll(e);
// System.out.println(e + "---");
for (Integer i : temp) {
// System.out.println(i + "*");
e.add(i);
Set ee = new HashSet();
ee.addAll(e);// 这里也是同理不能用等号,这里没有直接添加e是因为我在后面会改变e但是e在后面变了也会导致我添加进来的e改变
temp1.add(ee);// 这里又申请了一个,是因为我在用这样遍历方法遍历set的过程中改变set大小的话会改变它内部的一个计数的变量,这样会导致遍历出错
// 可以用iterator的方法修改变量
// System.out.println(e + "[[[");
e.remove(i);
}
temp.addAll(all);
// System.out.println(temp + ";");
// for (Integer s : temp) {
// System.out.print(s + " -");
// }
}
// for (Set e : temp1) {
// System.out.println(e);
// }
set.addAll(temp1);
}
for (Set e : set) {
for (Integer s : e) {
System.out.print(s + " ");
}
System.out.println();
}
}
}
这道题可谓是在集合这一块错误百出,还有将e放入集合s中如果在后面改变了e那么集合s中的e也会改变。
处理输入
用空格分割的字符串
import java.util.Scanner;
public class 输入 {
public static void main(String[] args) {
// f();//提取被一个空格分隔的字符串
f1();// 提取被不规则空格个数分割的字符串
}
private static void f1() {
// TODO Auto-generated method stub
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
scanner.nextLine();
while ((n--) > 0) {
String string = scanner.nextLine();
String[] s = string.split("\\s+");// 使用正则表达式将字符串分割 “\\s+”表示多个空格
for (int i = 0; i < s.length; i++) {
System.out.print(s[i] + " ");
}
System.out.println();
System.out.println("-----------------------");
}
}
private static void f() {
// TODO Auto-generated method stub
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
scanner.nextLine();// 吸收换行符!!!!!!!!一定不能漏了
while ((n--) > 0) {
String string = scanner.nextLine();
String[] s = string.split(" ");// 只能处理两数之间有一个空格
for (int i = 0; i < s.length; i++) {
System.out.print(s[i] + " ");
}
System.out.println("-----------------------");
}
}
}
字符串
替换字符
replace是替换字符
我大体说一下
public class 字符串 {
public static void main(String[] args) {
String string = "abcdefghi";
string = string.replace('c', 'b');
System.out.println(string);
string = string.replaceAll("b", "a");
System.out.println(string);
string = string.replaceFirst("a", "g");
System.out.println(string);
string = string.replace("aa", "hh");
System.out.println(string);
String myName1 = "domanokz";
String newName1 = myName1.substring(0, 4) + 'x' + myName1.substring(5);
System.out.println(newName1);
StringBuilder myName = new StringBuilder("domanokz");
myName.setCharAt(4, 'x');
System.out.println(myName);
System.out.println("--------------------------------");
string = "abcdefghi";
System.out.println(string.indexOf('e'));// 从头开始查找
string = "eabcdefghi";
System.out.println(string.indexOf('e', 1));// 下标为1处开始查找
}
}
格式化输出
System.out.println(year + "-" + String.format("%02d", mouth) + "-" + String.format("%02d", day));
两位数,缺少的前面补0
必记定律
// 大数开根号,字符串查找(解析)
private static BigInteger sqrt2(String s) {
int len = 0;
if (s.length() % 2 == 0) {
len = s.length() / 2;
} else {
len = s.length() / 2 + 1;
}
char[] arr = new char[len];
Arrays.fill(arr, '0');
BigInteger sBigInteger = new BigInteger(s);
for (int pos = 0; pos < len; pos++) {
for (char i = '1'; i <= '9'; i++) {
arr[pos] = i;
BigInteger res = new BigInteger(String.valueOf(arr)).pow(2);
if (res.compareTo(sBigInteger) == 1) {
// arr[pos] = (char) (arr[pos] - 1);
arr[pos] -= 1;
break;
}
}
}
return new BigInteger(String.valueOf(arr));
}
// 大数开根号,折半查找法(自己写的)
private static BigInteger sqrt1(BigInteger n) {
// TODO Auto-generated method stub
BigInteger l = BigInteger.ONE;
BigInteger r = n;
BigInteger mid = BigInteger.ZERO;
while (l.compareTo(r) != 1) {
mid = (l.add(r)).divide(BigInteger.valueOf(2));
// System.out.println(mid);
if (mid.multiply(mid).compareTo(n) == 0) {
break;
}
if (mid.multiply(mid).compareTo(n) == 1) {
r = mid.subtract(BigInteger.ONE);
} else {
l = mid.add(BigInteger.ONE);
}
}
if (mid.multiply(mid) != n && l.multiply(l).compareTo(n) == 1) {
l = l.subtract(BigInteger.ONE);//减了1之后就一定保证了l的平方比n小,这倒是值得推敲
//!!!!!!!!!!!!!!!!!!!!上面这一步很关键,退出的时候l平方可能小于大于n等于n,我们向下取,所以大于n的话要减1
}
mid = l;
return mid;
}
全排列
不详述思想,只说注意问题
看看2015第7题牌型种数,注意全排列的退出条件
2016第5题抽签 有重复的全排列
快速幂
快速幂的思想:在不超过要求幂的基础上,尽量以自身倍数的速度变化。
思想很简单,但是重点是落实在代码实现上,代码实现上,这里就又又又利用了二进制,
简单的代码
就是每次判断一下我把这个数变为倍数后是否会超出原来的次幂,如果每超出就x=x*x;
二进制2的10次幂,
把10写成二进制就是1010,因为10=8+2,所以我们需要2的8次幂乘2的2次幂,在上一中做法中,其实我们之前求过一次2的2次方,但是没有用到,后来还要再求,那么我们可以在求倍数的过程中先找到2的2次幂乘上结果,然后再找到2的8次幂乘上结果,最后就是结果,而对应二进制,就是每次倍数的时候,看幂的二进制的最后一位是否为1,如果是1那么结果乘上当前数,然后接着倍数,同时幂的二进制要右移,始终保持幂的二进制和我当前倍数表示的是一致的。
矩阵的快速幂(和普通快速幂没有区别,只是矩阵的写起来有点小麻烦)
public class 快速幂 {
static int res = 1;
static long[][] cnt = { { 0, 1 }, { 1, 0 } };
public static void main(String[] args) {
System.out.println(fib(6));
}
private static long fib(int i) {
// TODO Auto-generated method stub
if (i == 1 || i == 2) {
return 1;
}
long[][] m = { { 0, 1 }, { 1, 1 } };
long[] res = new long[2];
mi(m, i - 2);
long[] f = { 1, 1 };
res = mul(f, cnt);
return res[1];
}
private static void mi(long[][] m, int i) {
if (i == 0) {
return;
}
if ((i & 1) == 1) {
cnt = mul(cnt, m);
}
m = mul(m, m);
i = i / 2;
mi(m, i);
}
private static long[][] mul(long[][] f, long[][] m) {
long[][] res = new long[f.length][m[0].length];
for (int i = 0; i < m.length; i++) {// 遍历第一个矩阵的列
for (int j = 0; j < res.length; j++) {// 第二个矩阵的行
long sum = 0;
for (int k = 0; k < m.length; k++) {// 遍历第二个矩阵的行和第一个矩阵的列
sum += f[i][k] * m[k][j];
}
res[i][j] = sum;
}
}
return res;
}
private static long[] mul(long[] f, long[][] m) {
long[] res = new long[m[0].length];
for (int j = 0; j < res.length; j++) {// 第二个矩阵的行
long sum = 0;
for (int k = 0; k < m.length; k++) {// 遍历第二个矩阵的行和第一个矩阵的列
sum += f[k] * m[k][j];
}
res[j] = sum;
}
return res;
}
private static void f(int i, int j) {// 11
if (j == 0) {
return;
}
if ((j & 1) == 1) {
res *= i;
}
i = i * i;
j = j / 2;
f(i, j);
}
}
简单数的快速幂
public class 快速幂 {
static int res = 1;
public static void main(String[] args) {
f(2, 3);
System.out.println(res);
}
private static void f(int i, int j) {// 11
if (j == 0) {
return;
}
if ((j & 1) == 1) {
res *= i;
}
i = i * i;
j = j / 2;
f(i, j);
}
}
卢卡斯定理求组合数
通过上面这个给例子可以很好的理解这个定理:简单一句话概括就是组合数取余(余数为k)的结果,等于把上下两个数求k进制后对应的数字求组合数后再取余。
来自我的笔记dp之组合数问题
乘法逆元+快速幂求组合数
2018第十题堆的计数,这一题用到了逆元是因为我要取模,但是阶乘可能本身就很大要随时取模,但是除法不能直接像乘法一样,要用逆元。
a的逆元是a的p-2次方
阶乘的逆元可以分开求
for (int i = 1; i <= n; i++) {
f[i] = f[i - 1] * i % mod;// 求阶乘
inv[i] = pow(i, mod - 2) * inv[i - 1] % mod;// 这样写就是优化了一些,i的阶乘的逆元等于i的逆元*(i-1)的阶乘的逆元????
// inv[i] = pow(f[i], mod - 2) % mod;//a的逆元是a的p-2
}
f表示i的阶乘,inv表示i的阶乘的逆元
被注释掉的是直接求的,它上面那个是分开求的,分开求的原理见下图,
康托展开和逆康托展开
全排列中结果按照字典序排序第几位
树状数组,线段树,前缀和
树状数组是利用二进制,最关键的函数是return x&-x;
线段树是二分,要写一个树的类,然后要建树,不能少。
带权并查集
并查集加上了权值,这个权值是节点到父节点的权值大小。要注意权值的定义
核心不变的代码
private static int find(int b) {
// TODO Auto-generated method stub
if (b != fa[b]) {
int f = fa[b];
fa[b] = find(fa[b]);
dis[b] += dis[f];
}
return fa[b];
}
最短路径
Dijkstra算法
初始化最开始的路径,然后每次选取当前路径中最短的一个即优中选优,用这个路径向下扩充路径。
一共需要两层循环,第一层for循环表示我总共需要选出来n-1条路径,第二层for循环有两个,第一个选取当前路径中的最短路径,第二个用当前选出来的路径向下扩充路径
Floyed算法
核心代码:
SPFA算法
spfa运用了优先队列而非贪心
以上三种算法在if中要加上map[u][i] != Integer.MAX_VALUE不然会错,但是我不知道为什么?嗷嗷我知道了,是因为在初始化时用的是Integer.MAX_VALUE,而Integer.MAX_VALUE已经是最大了,再多加一点就变成负的了,反而成为最小的了,所以要判断一下再确定要不要加在一起。
最小生成树
dp模型
for{
if(a[i]>a[j]){
dp[i]=max(dp[i],dp[j]+1);
}
}
dp[i][j]=max(dp[i-1][j],dp[i][j-1])
if(s1[i]==s2[j]){
dp[i][j]=max(dp[i][j],dp[i-1][j-1]+1)
}
dp[i][j]=0
if(s1[i]==s2[j]){
dp[i][j]=max(dp[i][j],dp[i-1][j-1]+1)
}
贪心模型
2021
只做出来三道,唉
2020
2018
2016
煤球数目
递推+读题
生日蜡烛
暴力枚举
凑算式
暴力枚举
分小组
全排列
抽签
有重复的全排列
方格填数
全排列+二维化一维
剪邮票
四平方和
特殊的降低暴力复杂度的方法
取球博弈
模拟+dfs+记忆
关键在于记忆,还有如何实现身份的互换
压缩变换
数学分析+大数开根号
2015
2014
今年终于算是结束了蓝桥杯的比赛,以自己比较满意的成绩,从大一到大二,两次比赛,最终以幸运结尾。
以上只是我在临近省赛时做的总结,内容很大但不细节,如果有什么关于算法或蓝桥杯赛题的疑问,欢迎来我公众号问我,虽然我很菜,但是我们一起成长呀,加油!!!!!!!!!!