题目链接:
Q1
蓝桥云课:特殊日期
Q2
蓝桥云课:与或异或
洛谷:P13877 [蓝桥杯 2023 省 Java A] 与或异或
Q3
蓝桥云课:平均
洛谷:P13878 [蓝桥杯 2023 省 Java/Python A] 平均
算法原理:
Q1解法:暴力枚举
时间复杂度O(1)
枚举每个年份、每个月份、每个天数,算出各个数位上的总和进行比较
其中2月份比较特殊,按照闰年的计算规则:四年一闰,百年不闰,四百年再闰
得出判断闰年的代码:(i%4==0&&i%100!=0)||(i%400==0)
Q2解法:递归、搜索与回溯
时间复杂度O(3¹⁰)
将题目的示例转化成数组
会变成
java1 0 1 0 1 //第0行 1 1 1 0 //第1行 1 1 1 //第2行 1 0 //第3行 1 //第4行我们发现第1行第1个元素是由第0行第1个元素和第0行第2个元素决定的
第1行第2个元素是由第0行第2个元素和第0行第3个元素决定的
......
第2行第1个元素是由第1行第1个元素和第1行第2个元素决定的
......
因此大规模的问题可以分成相似的子问题来解决,所以我们采用递归的解法
由于针对每两个数有3种选择方式:&、|、^,因此我们要采用回溯的方法来逐个尝试当前位置分别采用&、|、^得出的结果
其实这里类似用到了隐式回溯,因为在数组中可以直接覆盖掉数据,达到"回溯"的效果
类似题目如下👇
A.每日一题:1415. 长度为 n 的开心字符串中字典序第 k 小的字符串
E.位运算-基础------3211. 生成不含相邻零的二进制字符串
A.每日一题:3129. 找出所有稳定的二进制数组 I +3130. 找出所有稳定的二进制数组 II
显式回溯与隐式回溯的区别
底层都是回溯思想,只是写法随容器变而已
①固定长度结构(char []、数组)→ 天然适合隐式回溯,写法更干净
②变长结构(StringBuffer、List、Builder)→ 必须显式回溯,不然必错
具体步骤:
Ⅰ按题目要求初始化第0行
Ⅱdfs方法设计:
①如果到了第5行,说明第4行第一个数即为结果,如果是1,方案数+1
②填写当前位置ret[i][j]的值:取决于ret[i-1][j]和ret[i-1][j+1]
分别填写ret[i-1][j]&ret[i-1][j+1]、ret[i-1][j] | ret[i-1][j+1]、ret[i-1][j]^ret[i-1][j+1]的结果
③填写完后判断当前位置是否是当前行的最后一个位置
如果是最后一个位置:开启下一行,从第1个位置开始填,否则就继续填当前行的下一个位置
Q3解法:贪心
时间复杂度O(n logn)
思路很简单,我们要保证0~9每个数都出现n/10次,那么为了保证总的代价和最小,肯定要移除代价小的
因此我们做出决定:
①用大小为10的数组表示数字0~9,将它们出现的代价挂在后面
②为了保留代价大的,移除代价小的,通过降序排序实现
图解如下👇
其中对顺序表降序排序直接用:Collections.sort(nums[i],Collections.reverseOrder());
关键易错点复盘
①创建大小为10的数组,其中每个元素是一个顺序表(此题的形式):
javaList<Integer>[] nums=new ArrayList[10];及时初始化,创建空顺序表:
javafor(int i=0;i<10;i++) nums[i]=new ArrayList<>();②创建一个顺序表,其中每个元素是大小为10的数组:
javaList<int[]> list=new ArrayList<>();及时初始化,添加数组
javalist.add(new int[10]);//第一个元素 list.add(new int[10]);//第二个元素 list.add(new int[10]);//第三个元素 //赋值过程 list.get(0)[5]=999; list.get(1)[9]=666;
Java代码:
Q1
java
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
int ret=0;
for(int i=1900;i<9999;i++){//枚举年份
//计算年份各位的和
String s=i+"";
int sum=0;
for(char c:s.toCharArray()) sum+=c-'0';
for(int j=1;j<=12;j++){//枚举月份
int n=0;//确定天数
if(j==1||j==3||j==5||j==7||j==8||j==10||j==12) n=31;
else if(j==2){
//闰年
boolean isleap=(i%4==0&&i%100!=0)||(i%400==0);
if(isleap) n=29;
//平年
else n=28;
}else n=30;
for(int k=1;k<=n;k++){
if(sum==getsum(j)+getsum(k)) ret++;
}
}
}
System.out.println(ret);
}
//获取各数位上的和
private static int getsum(int n){
int ret=0;
while(n>0){
ret+=n%10;
n/=10;
}
return ret;
}
}
Q2
java
import java.util.*;
public class Main{
private static int cnt=0;
public static void main(String[] args){
int[][] ret=new int[5][5];
ret[0][0]=1;
ret[0][1]=0;
ret[0][2]=1;
ret[0][3]=0;
ret[0][4]=1;
//从第1行第0个数开始计算
dfs(ret,1,0);
System.out.println(cnt);
}
private static void dfs(int[][] ret,int i,int j){
if(i==5){
cnt+=ret[4][0]==1?1:0;
return;
}
//k=0:& k=1:| k=2:^
for(int k=0;k<3;k++){
if(k==0) ret[i][j]=ret[i-1][j]&ret[i-1][j+1];
else if(k==1) ret[i][j]=ret[i-1][j]|ret[i-1][j+1];
else ret[i][j]=ret[i-1][j]^ret[i-1][j+1];
//判断j是不是当前行最后一个节点
if(j==4-i) dfs(ret,i+1,0);
else dfs(ret,i,j+1);
}
}
}
Q3
java
import java.util.*;
public class Main{
public static void main(String[] args){
Scanner sc=new Scanner(System.in);
int n=sc.nextInt();
int t=n/10;
long ret=0;
List<Integer>[] nums=new ArrayList[10];
//初始化每个位置为一个空顺序表
for(int i=0;i<10;i++) nums[i]=new ArrayList<>();
for(int i=0;i<n;i++){
int a=sc.nextInt();
int b=sc.nextInt();
nums[a].add(b);
}
//对数组中的每个顺序表降序排序
for(int i=0;i<10;i++){
if(nums[i].size()<=t) continue;
Collections.sort(nums[i],Collections.reverseOrder());
for(int j=t;j<nums[i].size();j++) ret+=nums[i].get(j);
}
System.out.println(ret);
sc.close();
}
}




