冲刺题单
基础
一、简单模拟(循环数组日期进制)
(一)日期模拟
知识点
1.把月份写为数组,二月默认为28天。
2.写一个判断闰年的方法,然后循环年份的时候判断并更新二月的天数
3.对于星期数的计算:
int week = x;//x表示当前天数的星期数
week = week % 7 + 1;
1.艺术与蓝球–蓝桥19937
package datasimulation;
public class Test5 {
//每个月,2月先默认为28天
//数组下标从0开始,给monts【0】赋值0
static int[] months = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
//汉字笔画数
static int[] hz = {13, 1, 2, 3, 5, 4, 4, 2, 2, 2};
//判断闰年
static boolean isLeapYear(int year){
//能整除400 或者 能整除4不能整除100
return (year % 400 == 0) || ((year % 4 == 0) && (year % 100 != 0));
}
static void solve(){
int ans = 0;//记录打篮球的天数
for(int y = 2000; y <= 2024; y++){
//重判二月的天数
months[2] = isLeapYear(y) ? 29 : 28;
for(int m = 1; m <= 12; m++){
for(int d = 1; d <= months[m]; d++){
int sum = 0;//记录权值
//获得每一个数
int y1 = y / 1000;
int y2 = y / 100 % 10;
int y3 = y / 10 % 10;
int y4 = y % 10;
int m1 = m / 10;
int m2 = m % 10;
int d1 = d / 10;
int d2 = d % 10;
sum = hz[y1] + hz[y2] + hz[y3] + hz[y4]
+ hz[m1] + hz[m2]
+hz[d1] + hz[d2];
//判断权值
if(sum > 50) ans++;
//循环结束条件
if(y == 2024 && m == 4 && d == 13){
System.out.println(ans);
return;//结束
}
}
}
}
}
public static void main(String[] args) {
solve();
}
}
//答案:3228
2.岁月流转–星期模拟–蓝桥16955
本题就和上面一样,不过多了个星期的问题,应用那两条公式即可。
package datasimulation;
public class Test6 {
//每个月,2月先默认为28天
//数组下标从0开始,给monts【0】赋值0
static int[] months = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
//判断闰年
static boolean isLeapYear(int year){
//能整除400 或者 能整除4不能整除100
return (year % 400 == 0) || ((year % 4 == 0) && (year % 100 != 0));
}
//获取1900年12月31日的星期数
static int getWeek(int week){
//1900年1.1是星期一,那么它前一天是星期日即7
months[2] = isLeapYear(1900) ? 29 : 28;
for (int m = 1; m <= 12; m++) {
for (int d = 1; d <= months[m]; d++) {
week = week % 7 + 1;
}
}
return week;
}
static void solve(){
int week = getWeek(7);获取1900年12月31日的星期数
int ans = 0;
for (int y = 1901; y <= 2000; y++) {
months[2] = isLeapYear(y) ? 29 : 28;
for (int m = 1; m <= 12; m++) {
for (int d = 1; d <= months[m]; d++) {
week = week % 7 + 1;
if(d == 1 && week == 7) ans++;
}
}
}
System.out.println(ans);
}
public static void main(String[] args) {
solve();
}
}
//171
3.跑步计划–日期星期模拟–蓝桥17113
public class Main {
//每个月,2月先默认为28天
//数组下标从0开始,给monts【0】赋值0
static int[] months = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
//判断闰年
static boolean isLeapYear(int year){
//能整除400 或者 能整除4不能整除100
return (year % 400 == 0) || ((year % 4 == 0) && (year % 100 != 0));
}
static void solve(){
int kl = 0;//跑步的公里数
//2023.1.1是星期日
int week = 6;
months[2] = isLeapYear(2023) ? 29 : 28;
for (int m = 1; m <= 12; m++) {
for (int d = 1; d <= months[m]; d++) {
//计算星期
week = week % 7 + 1;
//拆分数字
int m1 = m / 10;
int m2 = m % 10;
int d1 = d / 10;
int d2 = d % 10;
//只要年月日或者星期有1,就跑5千米
if(week == 1 || m1 == 1 || m2 == 1 || d1 == 1 || d2 == 1){
kl += 5;
}else{
kl += 1;
}
}
}
//输出公里数
System.out.println(kl);
}
public static void main(String[] args) {
solve();
}
}
//1333
4.一年中的第几天–蓝桥1935
package datasimulation;
import java.util.Scanner;
public class Test8 {
static int[] months = {0,31,28,31,30,31,30,31,31,30,31,30,31};
//判断闰年
static boolean isLeapYear(int y){
return (y % 400 == 0) || ((y % 4 == 0) && (y % 100 != 0));
}
//主逻辑函数
static void solve(int y, int m, int d){
int day = 0;//表示第几天
//更新2月的天数
months[2] = isLeapYear(y) ? 29 : 28;
for (int i = 1; i <= 12; i++) {//月份数
for (int j = 1; j <= months[i]; j++) {//天数
day++;
if(i == m && j == d){
System.out.println(day);
return;//结束循环
}
}
}
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while(true){
int y = sc.nextInt();
int m = sc.nextInt();
int d = sc.nextInt();
if(y == 0 && m == 0 && d == 0) break;//结束
solve(y, m, d);
}
}
}
本题出现评测机段错误的原因:
将Scanner写在了while循环中,拿出来即可
因此获得经验:对于一些特定的语句,一定要注意只创建一次即可。
5.回文日期–蓝桥498
注意给定的N小于89991231但是循环的边界不应该是8999,而应该是9999
package datasimulation;
import java.util.Scanner;
public class Test9 {
static boolean flag1 = false;
static boolean flag2 = false;
static int[] months = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
//判断闰年
static boolean isLeapYear(int y) {
return (y % 400 == 0) || ((y % 4 == 0) && (y % 100 != 0));
}
static boolean check(int y, int m, int d) {
//获得每一位数
int y1 = y / 1000;
int y2 = y / 100 % 10;
int y3 = y / 10 % 10;
int y4 = y % 10;
int m1 = m / 10;
int m2 = m % 10;
int d1 = d / 10;
int d2 = d % 10;
//判断回文
if (flag1 == false) {
if (d2 == y1 && d1 == y2 && m2 == y3 && m1 == y4) {
flag1 = true;
if(m >= 10 && d >= 10){
System.out.println(y + "" + m + "" + d);
} else if (m >= 10 && d < 10) {
System.out.println(y + "" + m + "0" + d);
} else if (d >= 10 && m < 10) {
System.out.println(y + "0" + m + "" + d);
} else if (m < 10 && d < 10) {
System.out.println(y + "0" + m + "0" + d);
}
}
}
//判断ABABBABA型回文
int A = y1;
int B = y2;
if (flag2 == false) {
if (y3 == A && y4 == B && m1 == B && m2 == A && d1 == B && d2 == A) {
flag2 = true;
if(m >= 10 && d >= 10){
System.out.println(y + "" + m + "" + d);
} else if (m >= 10 && d < 10) {
System.out.println(y + "" + m + "0" + d);
} else if (d >= 10 && m < 10) {
System.out.println(y + "0" + m + "" + d);
} else if (m < 10 && d < 10) {
System.out.println(y + "0" + m + "0" + d);
}
}
}
//两个都判断结束退出
if (flag1 && flag2) {
return true;
}
return false;
}
static void solve () {
Scanner sc = new Scanner(System.in);
int N = sc.nextInt();
int year = N / 10000;
int month = N / 100 % 100;
int day = N % 100;
//先循环到这个月结束
months[2] = isLeapYear(year) ? 29 : 28;
for (int d = day + 1; d <= months[month]; d++) {
boolean f = check(year, month, d);
if(f){
return;//结束循环
}
}
//循环到这一年结束
months[2] = isLeapYear(year) ? 29 : 28;
for (int m = month + 1; m <= 12; m++) {
for (int d = 1; d <= months[m]; d++) {
boolean f = check(year, m, d );
if(f){
return;//结束循环
}
}
}
for (int y = year + 1; y <= 9999; y++) {
months[2] = isLeapYear(y) ? 29 : 28;
for (int m = 1; m <= 12; m++) {
for (int d = 1; d <= months[m]; d++) {
boolean f = check(y, m, d );
if(f){
return;//结束循环
}
}
}
}
}
public static void main (String[]args){
solve();
}
}
更简便的写法:
data等的使用
6.神奇的闹钟–蓝桥19730
(二)进制转换
知识点
1.1K进制转十进制
K进制转十进制:
对于十六进制,由于它的数字表示范围为0-9,A-Z(或者a-z),在转换时要注意。
因此在赋值存储时,将要转换的进制数使用字符串类型来存储。
由此在进行进制转换前,要先将字符串类型的进制数转为数字。
1.将字符转换为数字值:
利用ascii码来进行转换。
2.将进制数转换为十进制:
horner法则
模板
package jinzhizhuanhuan;
public class Test1 {
//将字符转为数字
static int calc(char c){
//大于10的数
if(c >= 'a'){//A或者a看题目要求
return 10 + (c - 'a');
}else {
return (c - '0');
}
}
//将给定的字符串转换为10进制
static int change(int k, String s){
int ans = 0;
for (int i = 0; i < s.length(); i++) {
ans = ans * k + calc(s.charAt(i));
}
return ans;
}
public static void main(String[] args) {
System.out.println( change(16,"4e"));//78
}
}
题目答案
package jinzhizhuanhuan;
import java.util.Scanner;
public class Test6 {
//得到位数
static int calc(char c){
if(c >= 'A'){
return 10 + (c - 'A');
}else{
return c - '0';
}
}
//k进制转10进制
//幂运算转为hornor运算
static int change(int k, String s){
int ans = 0;
for (int i = 0; i < s.length(); i++) {
ans = ans * k + calc(s.charAt(i));
}
return ans;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int x = sc.nextInt();
String s = sc.next();
System.out.println(change(x, s));
}
}
1.2十进制转K进制–洛谷B3619
**除K取余法**
注意得到所有的余数后要反向排列才是最后的结果。
模板
package jinzhizhuanhuan;
public class Test2 {
static String change(int x, int k){
//把x转为k进制
StringBuffer ans = new StringBuffer();
while(x != 0){
int t = x % k;//除k取余
if(t <= 9){//数字部分直接添加
ans.append((char)('0' + t));
}else{//字母部分从A开始
ans.append((char)('A' + (t - 10)));
}
x = x / k;//更新x
}
//除K取余后要记得反转
return ans.reverse().toString();
}
//十进制转K进制
static void solve(){
System.out.println(change(78, 16));
}
public static void main(String[] args) {
solve();
}
}
题目答案
import java.util.Scanner;
public class Main {
static String change(int x, int k){
//除K取余法
StringBuffer ans = new StringBuffer();
while(x != 0){
int t = x % k;//除k取余
if(t > 9){
//字母部分
ans.append((char)('A' + (t - 10)));
}else{
//数字部分
ans.append((char)('0' + t));
}
x = x / k;//更新x
}
//除k取余得到的是一个反向的
//最后要把它倒回来
return ans.reverse().toString();
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int x = sc.nextInt();
System.out.println(change(n, x));
}
}
2.穿越时空之门–蓝桥19701
在上面的基础上进行修改即可
还无需考虑16进制的字母
package jinzhizhuanhuan;
public class Test3 {
//十进制转K进制(不用担心十六进制的字母问题)
//除k取余法并求各位数之和
static int change(int x, int k){
int ans = 0;//记录各位数之和
while(x != 0){
int t = x % k;//除k取余,k的取值是2和4,那么t的取值小于等于4
ans += t;
x = x / k;//更新x
}
return ans;
}
//主逻辑函数
static void solve(){
int count = 0;
for (int i = 1; i <= 2024; i++) {
if(change(i, 2) == change(i, 4))
count++;
}
System.out.println(count);
}
public static void main(String[] args) {
solve();
}
}
3.九进制转十进制–蓝桥2095
就是前面模板直接套用就可以
package jinzhizhuanhuan;
public class Test4 {
static int calc(char c){
if(c >= 'A'){
return 10 + (c - 'A');
}else {
//数字
return c - '0';
}
}
static int change(int k,String s){
int ans = 0;
//k进制转10进制
//幂运算转换为hornor法则
for (int i = 0; i < s.length(); i++) {
ans = ans * k + calc(s.charAt(i));
}
return ans;
}
public static void main(String[] args) {
System.out.println(change(9, "2022"));
}
}
(三)枚举
1.好数–蓝桥19709
package meiju;
import java.util.Scanner;
public class Test1 {
static boolean check(int x){
int count = 1;//记录奇偶位数
while(x != 0){
int t = x % 10;
if(count % 2 != 0){//奇数位上不能是偶数
if(t % 2 == 0){
return false;
}
}
if(count % 2 == 0){//偶数位上不能是奇数
if(t % 2 != 0){
return false;
}
}
x = x / 10;//更新,即去掉最后一位
count++;//更新奇偶数位
}
//循环结束即是好数
return true;
}
static void solve(){
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int ans = 0;//记录个数
for (int i = 1; i <= n; i++) {
if(check(i)){
ans++;
}
}
System.out.println(ans);
}
public static void main(String[] args) {
solve();
}
}
2.偶串–蓝桥17144
主要涉及HashMap的使用
import java.util.HashMap;
import java.util.Scanner;
public class Main {
static int N = 1000010;
//定义一个字符数组
static HashMap<Character, Integer> charArr = new HashMap<Character, Integer>();
//判断是否都为偶数
static boolean check(HashMap<Character, Integer> arr){
for (int count : charArr.values()) {
if (count % 2 != 0) return false;//奇数返回错
}
return true;
}
static void solve(){
Scanner sc = new Scanner(System.in);
String s = sc.next();
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
//如果这个字符 c 已经存在,就获取它的计数,否则默认是 0。
charArr.put(c, charArr.getOrDefault(c, 0) + 1);
}
if(check(charArr)){
System.out.println("YES");
}else{
System.out.println("NO");
}
}
public static void main(String[] args) {
solve();
}
}
3.幸运数–蓝桥3491
填空题,代码是超时的。。。
import java.util.ArrayList;
public class Main {
// static int N = 15;//多一点防止奇怪的问题
// static boolean check(int x){
// ArrayList<Integer> numArr = new ArrayList<>();
// int count = 0;//记录x的位数
// while(x != 0){
// int t = x % 10;//获得最后一位
// numArr.add(t);//添加到list中,下标从0开始
// x = x / 10;//舍弃最后一位
// count++;
// }
// //先判断是不是偶数位
// if(count % 2 != 0){
// return false;
// }
// //是偶数位
// int mid = count / 2;
// int sum1 = 0;
// int sum2 = 0;
// for (int i = 0; i < mid; i++) {
// sum1 += numArr.get(i);
// }
// for (int i = mid; i < count; i++) {
// sum2 += numArr.get(i);
// }
// //判断前后是否相等
// if (sum1 != sum2){
// return false;
// }
// //最后是偶数位,前后相等
// return true;
// }
// static void solve(){
// int ans = 0;
//2位
// for (int i = 1; i <= 100; i++) {
// if(check(i)) ans++;
// }
//4位
// for (int i = 1000; i <= 9999; i++) {
// if(check(i)) ans++;
// }
//6位
// for (int i = 100000; i <= 999999; i++) {
// if(check(i)) ans++;
// }
//8位
// for (int i = 10000000; i <= 99999999; i++) {
// if(check(i)) ans++;
// }
// System.out.println(ans);
// }
public static void main(String[] args) {
System.out.println(4430091);
}
}
4.求和–蓝桥3493
简单的题目要特别注意数值范围和超时。
package meiju;
public class Test4 {
public static void main(String[] args) {
long ans = 0;
for (int i = 1; i <= 20230408; i++) {
ans += i;
}
System.out.println(ans);
}
}
5.不完整的算式–蓝桥17135
特别注意中英文符号
四种情况:又可大概分为2种
首先判断字符串中是否有+-*/
一、有,找到加减乘除的位置op
1.A被擦掉
0的位置是?,那么A被擦掉
获得B=substring(op,=)
获得C=subString(=)
2.B
op后面的位置是?,那么B被擦掉
获得A=substring(0,op)
获得C=subString(=)
3.C
=后面的位置是?,C被擦掉
获得A=substring(0,op)
获得B=substring(op,=)
二、没有
4.op
找到?的位置,截取出A,B,C即可
package com.mj;
import java.util.Scanner;
public class Main{
//主逻辑函数
static void solve() {
Scanner sc = new Scanner(System.in);
String s = sc.next();//输入等式
//首先判断等式中有无四则运算符号
//注意要依次判断
int haveOp = s.indexOf('+');
if(haveOp == -1) {
haveOp = s.indexOf('-');
}
if(haveOp == -1) {
haveOp = s.indexOf('*');
}
if(haveOp == -1) {
haveOp = s.indexOf('/');
}
//没有,那么获得A,B,C的值
if(haveOp == -1) {
int op = s.indexOf('?');//op的位置
int d = s.indexOf('=');
//注意substring包前不包后
int A = Integer.parseInt(s.substring(0,op));
int B = Integer.parseInt(s.substring(op + 1,d));
int C = Integer.parseInt(s.substring(d + 1));
if(A + B == C) {
System.out.println('+');
}else if(A - B == C) {
System.out.println('-');
}else if(A * B == C) {
System.out.println('*');
}else if(A / B == C) {
System.out.println('/');
}
return;
}
//有
//A被擦掉
if(s.charAt(0) == '?') {
int d = s.indexOf('=');
int B = Integer.parseInt(s.substring(haveOp + 1, d));
int C = Integer.parseInt(s.substring(d + 1));
if(s.charAt(haveOp) == '+') {
System.out.println(C - B);
}else if(s.charAt(haveOp) == '-') {
System.out.println(C + B);
}else if(s.charAt(haveOp) == '*') {
System.out.println(C / B);
}else if(s.charAt(haveOp) == '/') {
System.out.println(C * B);
}
return;
}
//B被擦掉
if(s.charAt(haveOp + 1) == '?') {
int d = s.indexOf('=');
int A = Integer.parseInt(s.substring(0, haveOp));
int C = Integer.parseInt(s.substring(d + 1));
if(s.charAt(haveOp) == '+') {
System.out.println(C - A);
}else if(s.charAt(haveOp) == '-') {
System.out.println(A - C);
}else if(s.charAt(haveOp) == '*') {
System.out.println(C / A);
}else if(s.charAt(haveOp) == '/') {
System.out.println(A / C);
}
return;
}
//C被擦掉
int d = s.indexOf('=');
int A = Integer.parseInt(s.substring(0, haveOp));
int B = Integer.parseInt(s.substring(haveOp + 1, d));
if(s.charAt(haveOp) == '+') {
System.out.println(A + B);
}else if(s.charAt(haveOp) == '-') {
System.out.println(A - B);
}else if(s.charAt(haveOp) == '*') {
System.out.println(A * B);
}else if(s.charAt(haveOp) == '/') {
System.out.println(A / B);
}
}
public static void main(String[] args){
solve();
}
}
(四)自定义排序
知识点
1.排序:
Arrays.sort 的自定义排序器只接受对象数组。使用整型对象数组(Integer[])
Arrays.sort(数组)
Arrays.sort(数组,开始,结束)//包前不包后
2.自定义比较器:
不理解
自定义排序注意点-CSDN博客
你用的是什么 | 排序方法 | 要求的类型 |
---|---|---|
int[] | Arrays.sort(arr) | 无法使用 Comparator |
Integer[] | Arrays.sort(arr, cmp) | ✅ 可以使用 Comparator |
ArrayList<T> | Collections.sort(list, cmp) | ✅ 正确 |
模板
import java.util.Arrays;
import java.util.Comparator;
public class Main {
// 1. 普通数组排序
static Integer[] nums = {3,1,4,1,5,9};
// 2. 对象排序
static class Student {
String name;
int score;
Student(String name, int score) {
this.name = name;
this.score = score;
}
}
public static void main(String[] args) {
// 匿名内部类方式
Arrays.sort(nums, new Comparator<Integer>() {
@Override
public int compare(Integer a, Integer b) {
return b - a; // 降序
}
});
// 学生数组
Student[] students = {new Student("Alice",90), new
Student("Bob",85)};
// 多级排序
Arrays.sort(students, new Comparator<Student>() {
@Override
public int compare(Student a, Student b) {
if(a.score != b.score)
return b.score - a.score; // 分数降序
return a.name.compareTo(b.name); // 姓名升序
}
});
}
}
1.区间排序
https://www.luogu.com.cn/problem/B4041
注意l和r是从1开始得
而数组下标是从0开始的
package com.mj;
import java.util.*;
public class Main{
// static int N = 100010;
//主逻辑函数
static void solve() {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
Integer[] a = new Integer[n];
for(int i = 0; i < n; i++) {
a[i] = sc.nextInt();
}
int q = sc.nextInt();
while(q != 0) {
int l = sc.nextInt() - 1;
int r = sc.nextInt() - 1;
//包前不包后
Arrays.sort(a, l, r + 1);
q--;
}
for(int i = 0; i < n; i++) {
System.out.print(a[i] + " ");
}
}
public static void main(String[] args){
solve();
}
}
2.奇偶排序
https://hydro.ac/d/shallowdream/p/38
对数组进行排序
自定义比较器:
判断奇偶性:
不同:偶数大于奇数
相同:实际数值大小比较
package com.mj;
import java.util.*;
public class Main{
// static int N = 100010;
//主逻辑函数
static void solve() {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
Integer[] num = new Integer[n];
for(int i = 0; i < n; i++) {
num[i] = sc.nextInt();
}
Arrays.sort(num, new Comparator<Integer>() {
public int compare(Integer a, Integer b) {
//判断奇偶性:
//不同:偶数大于奇数
//相同:实际数值大小比较
if(a % 2 == b % 2) {
//奇偶性相同,实际数值,由小到大
return a - b;
}else {
//不同:偶数大于奇数
//a是偶数,返回1,a大于b排后面
//a是奇数,返回-1,a小于b排前面
return (a % 2 == 0) ? 1 : -1;
}
}
});
//
for(int i = 0; i < n; i++) {
System.out.print(num[i] + " ");
}
}
public static void main(String[] args){
solve();
}
}
注意:最开始在外面创建数组,创建得很大
而排序是对整个数组进行排序
因此报空指针错误
所以要在里面进行一个创建数组并排序。
还要特别注意,普通数组不能自定义排序
Integer数组对应Arrays.sort
ArrayList数组对应Collections.sort
3.平面最近点对:
https://hydro.ac/d/shallowdream/p/37
package com.mj;
import java.util.*;
public class Main{
static class zuobiao{
int x;
int y;
zuobiao(int x,int y){
this.x = x;
this.y = y;
}
}
//主逻辑函数
static void solve() {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
zuobiao[] arr = new zuobiao[n];//创建对象数组
for(int i = 0; i < n; i++) {
int x = sc.nextInt();
int y = sc.nextInt();
zuobiao t = new zuobiao(x,y);
arr[i] = t;
}
//自定义排序
Arrays.sort(arr, new Comparator<zuobiao>() {
public int compare(zuobiao a, zuobiao b) {
int x1 = a.x;
int y1 = a.y;
int x2 = b.x;
int y2 = b.y;
//先判断欧几里得距离
long s1 = (long)x1 * x1 + (long)y1 * y1;
long s2 = (long)x2 * x2 + (long)y2 * y2;
if(s1 != s2) {
if(s1 < s2) {
//降序排序,先排a
return -1;
}else {
return 1;
}
}
//再判断横坐标
if(x1 != x2) {
if(x1 < x2) {
return -1;//先排a
}else {
return 1;
}
}
//再判断纵坐标
//前面都没有才会运行此处,不if不然没有返回值
// if(y1 != y2) {
if(y1 < y2) {
return -1;//先排a
}else {
return 1;
}
}
});
//输出
for(int i = 0; i < n; i++) {
System.out.println(arr[i].x + " " + arr[i].y);
}
}
public static void main(String[] args){
solve();
}
}
虽然逻辑没有问题,但是超时了,超时的原因是输入使用Scanner。
所以修改如下。
//改用 BufferedReader + StringTokenizer
package com.mj;
import java.io.*;
import java.util.*;
public class Main {
static class Point {
int x, y;
Point(int x, int y) {
this.x = x;
this.y = y;
}
}
public static void main(String[] args) throws IOException {
// 快速输入
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int n = Integer.parseInt(br.readLine());
Point[] arr = new Point[n];
for (int i = 0; i < n; i++) {
StringTokenizer st = new StringTokenizer(br.readLine());
int x = Integer.parseInt(st.nextToken());
int y = Integer.parseInt(st.nextToken());
arr[i] = new Point(x, y);
}
// 排序
Arrays.sort(arr, new Comparator<Point>() {
public int compare(Point a, Point b) {
long d1 = 1L * a.x * a.x + 1L * a.y * a.y;
long d2 = 1L * b.x * b.x + 1L * b.y * b.y;
if (d1 != d2) return Long.compare(d1, d2);
if (a.x != b.x) return Integer.compare(a.x, b.x);
return Integer.compare(a.y, b.y);
}
});
// 快速输出
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
for (int i = 0; i < n; i++) {
bw.write(arr[i].x + " " + arr[i].y + "\n");
}
bw.flush(); // 一定要刷新
}
}
4.三国游戏:
https://www.lanqiao.cn/problems/3518/learning
5.拼数:
https://www.luogu.com.cn/problem/P1012
(四)自定义排序
1.蓝桥19723–分布式队列
package datasimulation;
import java.util.Scanner;
public class Test3 {
//计算数组中最小值
public static int getMin(int[] arr){
int min = arr[0];//最好设为数组中的某个值
for (int i = 0; i < arr.length; i++) {
if(arr[i] < min){
min = arr[i];
}
}
return min;
}
//主逻辑函数
public static void solve(){
Scanner sc = new Scanner(System.in);
//输入N
int N = sc.nextInt();
//创建一个长度为N的数组,数组元素表示的是每个节点数组的长度
int[] arr = new int[N];
//输入?行数据
while(sc.hasNext()){
String op = sc.next();
if(op.equals("add")){
int opNum = sc.nextInt();
//主节点数组长度加1
arr[0]++;
} else if (op.equals("sync")) {
int opNum = sc.nextInt();
//副节点数组长度加1
//!!!特别注意!!!
副队列同步主队列,如果在执行这次操作时副队列已经是跟主队列相同了,
// 此时再加1并不合理,最高就是跟主队列相同
//其实题目的用例有偷偷暗示
arr[opNum] = Math.min(arr[opNum] + 1,arr[0]);
}else{
//输出可见数
//其实就是输出arr数组中的最小值--各个节点数组的长度最小值
System.out.println(getMin(arr));
}
}
}
public static void main(String[] args) {
solve();
}
}
注意:
1.使用一个数组来描述,数组的元素表示的是每个节点数组的长度,可见数其实就是这个数组中最小的元素。
2.要特别注意观察题目给的样例,其中可能暗藏玄机。
最开始我只通过50%,后来才发现给的用例可能会重复,如果遇到sync只是一味加加,那么副节点数组的长度就会超过主节点,这就不对了。
3.学到了一个sc.hasNext()的使用。因为本题不知道输入的操作个数。
4.特别注意数据的输入格式。
2.蓝桥19709–好数
package datasimulation;
import java.util.Scanner;
public class Test4 {
public static boolean check(int n){
int count = 1;//表示奇偶数位
int jicount = 0;
int oucount = 0;
while(n != 0){
int t = n % 10;
n = n / 10;
if(count % 2 == 0 && t % 2 == 0){
oucount++;
}
if(count % 2 != 0 && t % 2 != 0){
jicount++;
}
count++;//下一位
}
//注意上面循环count多加了1
if(jicount + oucount + 1 == count) return true;
else return false;
}
//主逻辑函数
public static void solve(){
Scanner sc = new Scanner(System.in);
//输入N
int N = sc.nextInt();
int count = 0;//记录好数
for (int i = 1; i <= N; i++) {
if(check(i)) count++;
}
System.out.println(count);
}
public static void main(String[] args) {
solve();
}
}
//判断好数
//奇数位不能是偶数,偶数位不能是奇数
public static boolean check(int n){
int cnt = 1;//位数计数器
while(n != 0){
int t = n % 10;
if(cnt % 2 != 0){
//奇数位不能偶数
if(t % 2 == 0) return false;
}
if(cnt % 2 == 0){
//偶数位不能是奇数
if(t % 2 != 0) return false;
}
cnt++;//下一位数
n = n / 10;
}
return true;//都结束了返回true
}
学到的知识点:
本题使用暴力来做,额我就只想到对于奇数位是奇数的加1,偶数位是偶数的加一,因此要循环全部,效率较低。而且还有一点就是最好的count多加了1。
老师的解法就是奇数位偶数返回错,偶数位奇数返回错。这样提高了效率,也不需要注意太多。
二、前缀和与差分 要求:省二以上会
知识点
1.一维前缀和:s[i]=s[i-1]+a[i]
快速查询区间和。l-r的区间和就是s[r]-s[l-1]
2.一维差分:b[i]=a[i]-a[i-1]
差分数组求前缀和是原数组。
快速进行区间修改(加减)。对l-r的元素加上d,只要b[l]+=d,b[r+1]-=d即可。
以及重要的一点:判断序列元素是否相同,即差分数组除了第一项以外的项数均为0,那么原数组一定所有数字相同。
对于二维,画图更好理解
1.二维前缀和
规定的二维前缀和如下:从(1,1))开始到(x,y)所围成的一个子矩阵中的数的和
对于(x1,y1)到(x2,y2)围成的子矩阵的和,计算如下:
s(x2,y2)-x(x2,y1-1)-x(x1-1,y2)+x(x1-1,y1-1)
如何构造前缀和:
s(x,y)=a(x,y)+s(x-1,y)+s(x,y-1)-s(x-1,y-1)
2.二维差分
**前缀和数组求差分后是原数组,原数组求差分后是差分数组。**
**差分数组求前缀和是原数组,原数组求前缀和是前缀和数组。**
一定明确差分数组求前缀和是原数组。
对差分数组b[i][j]求前缀和,
而差分数组求前缀和就是原数组a[i][j],
所以套用前缀和公式,有:
a[i][j]=b[i][j]+a[i][j-1]+a[i-1][j]-a[i-1][j-1]
对其进行移项,就得到差分数组的公式:
b[i][j]=a[i][j]-a[i][j-1]-a[i-1][j]+a[i-1][j-1]
对一个差分数组,对其x1,y1进行修改,相当于对原数组x1,y1到右下角进行修改
b[x1][y1] += d;
b[x2 + 1][y1] -= d;
b[x1][y2+1] -= d;
b[x2+1][y2+1] += d;
1.1蓝桥18437–一维前缀和(模板
主要注意数据的范围,计算极端情况下数据的范围是否超过int。
package com.mj;
import java.util.Scanner;
public class Main{
static int N = 100010;
static int n,q;
static int[] a = new int[N];
static int[] s = new int[N];//前缀和数组
//主逻辑函数
static void solve() {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
q = sc.nextInt();
for(int i = 1; i <= n; i++) {
a[i] = sc.nextInt();
//计算前缀和数组
s[i] = s[i - 1] + a[i];
}
//q组查询
for(int i = 0; i < q; i++) {
int l = sc.nextInt();
int r = sc.nextInt();
System.out.println(s[r] - s[l - 1]);
}
}
public static void main(String[] args){
solve();
}
}
1.2.求和-蓝桥2080
其实也差不多是一道模板题目的变形。但这题就要注意数值范围了,有一个需要为long,其它不知道怎么搞那就都为long吧。防止溢出之类的。
找规律,提取公因数,最终得到每项都是
a[i]*(a[i+1] + …… + a[n])
1.3一维差分–模板–蓝桥18438
模板提。注意数值范围即可。
还要注意输出的格式。
package com.mj;
import java.util.Scanner;
public class Main{
static int N = 200010;
static int n,m;
static int[] a = new int[N];
static int[] b = new int[N];//差分数组
//主逻辑函数
static void solve() {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
m = sc.nextInt();
for(int i = 1; i <= n; i++) {
a[i] = sc.nextInt();
//计算差分数组
b[i] = a[i] - a[i - 1];
}
for(int i = 0; i < m; i ++) {
int l = sc.nextInt();
int r = sc.nextInt();
int d = sc.nextInt();
//对l-r的数增加d
b[l] += d;
b[r + 1] -= d;
}
//对差分数组求前缀和得到原数组
for(int i = 1; i <= n; i++) {
a[i] = a[i - 1] + b[i];
System.out.print(a[i] +" ");
}
}
public static void main(String[] args){
solve();
}
}
1.4小蓝的操作–利用差分数组判断序列元素是否相等–蓝桥3399
本题主要还是思维的问题。要将操作的问题进行一个转换。
因为是选择区间进行减一操作,且数据保证一定有解。
那么对于差分数组而言,要让除了第一项外的大于0的数都为0,第一项为1。
把这些数加起来就是操作数。
注意数值范围:
分析3:数值范围的分析
对于差分数组,若原数组最坏情况下为1 100000 1 100000这么排列,那么差分数组可能有一半的数量为99999,那么n的一半为5*10^4,相乘大于4.5*10^9,大于int的范围。所以ans需要为long类型。
package com.mj;
import java.util.Scanner;
public class Main{
static int N = 100010;
static int n;
static long[] a = new long[N];
static long[] b = new long[N];//差分数组
//主逻辑函数
static void solve() {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
for(int i = 1; i <= n; i++) {
a[i] = sc.nextInt();
//计算差分数组
b[i] = a[i] - a[i - 1];
}
long ans = 0;//操作数
//差分数组第一项为1
ans = b[1] - 1;//第一项操作几次变为1
for(int i = 2; i <= n; i++) {
if(b[i] > 0) {
ans += b[i];//b[i]操作几次变为0
}
}
System.out.print(ans);
}
public static void main(String[] args){
solve();
}
}
1.5二维前缀和–模板–蓝桥18439
模板提。
主要判断数值范围。
package com.mj;
import java.util.Scanner;
public class Main{
static int N = 1010;
static int n,m,q;
static int[][] a = new int[N][N];
static int[][] s = new int[N][N];//前缀和数组
//主逻辑函数
static void solve() {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
m = sc.nextInt();
q = sc.nextInt();
//输入数组
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++) {
a[i][j] = sc.nextInt();
//计算前缀和
s[i][j] = a[i][j] + s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1];
}
}
//q组查询
for(int i = 0; i < q; i++) {
int x1 = sc.nextInt();
int y1 = sc.nextInt();
int x2 = sc.nextInt();
int y2 = sc.nextInt();
System.out.println(s[x2][y2] - s[x2][y1 - 1] - s[x1 - 1][y2] + s[x1 -1][y1 -1]);
}
}
public static void main(String[] args){
solve();
}
}
1.6子矩阵修改问题–蓝桥18440
二维差分模板题目
也是注意数值范围即可
package com.mj;
import java.util.Scanner;
public class Main{
static int N = 1010;
static int n,m,q;
static int[][] a = new int[N][N];
static int[][] b = new int[N][N];//差分数组
//主逻辑函数
static void solve() {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
m = sc.nextInt();
q = sc.nextInt();
//输入数组
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++) {
a[i][j] = sc.nextInt();
//计算差分数组
//前缀和s[i][j] = a[i][j] + s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1];
b[i][j] = a[i][j] - a[i - 1][j] - a[i][j - 1] + a[i - 1][j - 1];
}
}
//q组查询
for(int i = 0; i < q; i++) {
int x1 = sc.nextInt();
int y1 = sc.nextInt();
int x2 = sc.nextInt();
int y2 = sc.nextInt();
int d = sc.nextInt();
b[x1][y1] += d;
b[x2 + 1][y1] -= d;
b[x1][y2+1] -= d;
b[x2+1][y2+1] += d;
}
//输出二维数组
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++) {
//计算差分数组的前缀和
a[i][j] = b[i][j] + a[i - 1][j] + a[i][j - 1] - a[i - 1][j - 1];
System.out.print(a[i][j] + " ");
}
System.out.println();
}
}
public static void main(String[] args){
solve();
}
}
https://www.lanqiao.cn/problems/18439/learning/
https://www.lanqiao.cn/problems/18438/learning/
https://www.lanqiao.cn/problems/18440/learning/
https://www.lanqiao.cn/problems/19716/learning/
https://www.lanqiao.cn/problems/19703/learning/
https://www.lanqiao.cn/problems/3533/learning/
https://www.lanqiao.cn/problems/3514/learning
三、二分
知识点
二分:
1.序列二分:在序列中查找(不怎么考,会比较难?)
序列二分应用的序列必须是递增或递减,但可以非严格
只要r是mid-1,就对应mid=(l+r+1)/2
2.答案二分:最值
答案二分更重要的是思路,要自己可以二分的点,一般先写暴力,再将其转为二分。
3.浮点数二分:连续性
模板题目
1.1序列二分模板题–蓝桥18492
模板题目
package erfen;
import java.util.Scanner;
public class Test1 {
static int N = 100010;
static int n, q;
static int[] a = new int[N];
//找到等于x的最左边的数的下标
static int getL(int l, int r, int x) {
while (l < r) {
int mid = l + r >> 1;//右移1表示除以2
if (a[mid] >= x) r = mid;
else l = mid + 1;
}
if (a[l] == x)
return l;//返回下标
else
return -1;//不存在
}
//找到等于x的最右边的数的下标
static int getR(int l, int r, int x) {
while (l < r) {
int mid = l + r + 1 >> 1;//右移1表示除以2
if (a[mid] <= x) l = mid;
else r = mid - 1;
}
if (a[l] == x)
return l;//返回下标
else
return -1;//不存在
}
//找到大于等于x的第一个数的下标
static int lower_bound(int l, int r, int x) {
while (l < r) {
int mid = l + r >> 1;//右移1表示除以2
if (a[mid] >= x) r = mid;
else l = mid + 1;
}
if (a[l] >= x)
return l;//返回下标
else
return -1;//不存在
}
//找到大于x的第一个数的下标
static int upper_bound(int l, int r, int x) {
while (l < r) {
int mid = l + r >> 1;//右移1表示除以2
if (a[mid] > x) r = mid;
else l = mid + 1;
}
if (a[l] > x)
return l;//返回下标
else
return -1;//不存在
}
//主逻辑函数
static void solve() {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
q = sc.nextInt();
for (int i = 1; i <= n; i++) {
a[i] = sc.nextInt();
}
for (int i = 0; i < q; i++) {
int op = sc.nextInt();
int l = sc.nextInt();
int r = sc.nextInt();
int x = sc.nextInt();
if (op == 1) {
System.out.println(getL(l, r, x));
} else if (op == 2) {
System.out.println(getR(l, r, x));
} else if (op == 3) {
System.out.println(lower_bound(l, r, x));
} else if (op == 4) {
System.out.println(upper_bound(l, r, x));
}
}
}
public static void main(String[] args) {
solve();
}
}
1.2最大通过数–蓝桥3346–前缀和+二分
本题利用了前缀和和二分
思想很重要,首先枚举左边,再在里面枚举右边的通过数。
先写暴力然后就很容易写二分。
注意数值范围
暴力
package erfen;
import java.util.Scanner;
public class Test2 {
static int N = 200010;
static int m,n;
static long k;
static long[] a = new long[N];
static long[] b = new long[N];
//主逻辑函数
static void solve(){
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
m = sc.nextInt();
k = sc.nextLong();
for (int i = 1; i <= n; i++) {
a[i] = sc.nextLong();
a[i] = a[i - 1] + a[i];//计算前缀和
}
for (int i = 1; i <= m; i++) {
b[i] = sc.nextLong();
b[i] = b[i - 1] + b[i];//计算前缀和
}
int ans = 0;
for (int i = 0; i <= n; i++) {
//循环左边
if(a[i] > k) break;//a[0]为0,所以无需担心左边为0没有遍历右边
long x = k - a[i];//左边可通过i关
for (int j = 0; j <= m; j++) {
if(x >= b[j]){//右边可通过j关
ans = Math.max(ans, i + j);
}else{
break;
}
}
}
System.out.println(ans);
}
public static void main(String[] args) {
solve();
}
}
二分
计算前缀和数组,它是递增的,可以应用二分
要二分就要找到单调的趋势,找到可以二分的点。
package erfen;
import java.util.Scanner;
public class Test2 {
static int N = 200010;
static int m,n;
static long k;
static long[] a = new long[N];
static long[] b = new long[N];
//主逻辑函数
static void solve(){
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
m = sc.nextInt();
k = sc.nextLong();
for (int i = 1; i <= n; i++) {
a[i] = sc.nextLong();
a[i] = a[i - 1] + a[i];//计算前缀和
}
for (int i = 1; i <= m; i++) {
b[i] = sc.nextLong();
b[i] = b[i - 1] + b[i];//计算前缀和
}
int ans = 0;
for (int i = 0; i <= n; i++) {
//枚举左边
if(a[i] > k) break;//a[0]为0,所以无需担心左边为0没有遍历右边
long x = k - a[i];//剩下的
//二分第二个山洞,找到大于x的第一个数
//因为是找大于x的第一个数,x可能是最后一个即m,所以r的取值要是m+1
int l = 0, r = m + 1;
while(l < r){
int mid = l + r >> 1;
if(b[mid] > x) r = mid;
else l = mid + 1;
}
ans = Math.max(ans, i + l - 1);
}
System.out.println(ans);
}
public static void main(String[] args) {
solve();
}
}
1.3答案二分模板–https://hydro.ac/d/shallowdream/p/33
求最小的最大值,这题和后面那道数列分段一样,都是要求一个最值。
这个最值是自己进行枚举来得到的,这是一个重要的思路。
第二个就是本题的k次加一操作转换为了元素要达到max需要消耗多少k值,由此找到最小的max
暴力
package erfen;
import java.util.Scanner;
public class Test3 {
static int N = 100010;
static int n;
static long k;
static int[] a = new int[N];
static void solve(){
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
k = sc.nextLong();
for (int i = 1; i <= n; i++) {
a[i] = sc.nextInt();
}
//枚举最小值
for (int i = 1; i <= 1e14; i++) {
long t = k;//每次都要重新初始化
for (int j = 1; j <= n; j++) {
//循环数组每个元素看能否达到i
if(a[j] < i){
t = t -(i - a[j]);//a[j]需要(i - a[j])次加一操作才能达到i
}
//结束后进行判断
if(t < 0){
//当前数组某个元素不能达到i,那整个数组都不能达到i
System.out.println(i - 1);
return;//结束全部
}
}
}
}
public static void main(String[] args) {
solve();
}
}
二分
找到可以二分的点:随着枚举的最大的最小值越大,t的值即剩余的k操作越来越少。
package erfen;
import java.util.Scanner;
public class Test3 {
static int N = 100010;
static int n;
static long k;
static int[] a = new int[N];
//
static boolean check(long m){
long t = k;//每次都要重新初始化
for (int j = 1; j <= n; j++) {
//循环数组每个元素看能否达到m
if(a[j] < m){
t = t -(m - a[j]);//a[j]需要(m - a[j])次加一操作才能达到m
}
//结束后进行判断
if(t < 0){
//当前数组某个元素不能达到i,那整个数组都不能达到i
/*System.out.println(i - 1);*/
return false;//结束全部
}
}
//整个循环结束,表示整个数组可以达到m
return true;
}
static void solve(){
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
k = sc.nextLong();
for (int i = 1; i <= n; i++) {
a[i] = sc.nextInt();
}
//二分最大的最小值
long l = 1;
long r = (long)1e14;
while(l < r){
long mid = l + r + 1 >> 1;
if(check(mid)) l = mid;//true,表示可以达到mid,但是这个mid不一定是最大的。所以让l=mid。
else r = mid - 1;
}
System.out.println(l);
}
public static void main(String[] args) {
solve();
}
}
1.4数列分段
本题的难点在于分段
思路:
1.枚举最小的最大值
2.分段的思想,采用cnt来记录段数,sum来记录当前段的和。
暴力
package erfen;
import java.util.Scanner;
public class Test4 {
static int N = 100010;
static int n,m;
static int[] a = new int[N];
//判断能否做最小的最大值
static boolean check(int x){
int sum = 0;//记录当前段和
int cnt = 1;//记录段数,最小也为1
for (int i = 1; i <= n; i++) {
if(sum + a[i] <= x){
//当前段还可以继续加
sum = sum + a[i];
}else{
sum = a[i];//另开一段
cnt++;//段数加一
}
}
return cnt <= m;//只有cnt大于m,才说明这个x不是最小的最大值
//可以等于是因为元素可能较小,m给的大。
}
//主逻辑函数
static void solve(){
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
m = sc.nextInt();
for (int i = 1; i <= n; i++) {
a[i] = sc.nextInt();
}
//枚举最小的最大值
for (int i = 1; i <= 1e9; i++) {
if(check(i)) {
System.out.println(i);
return;//结束
}
}
}
public static void main(String[] args) {
solve();
}
}
二分
package erfen;
import java.util.Scanner;
public class Test4 {
static int N = 100010;
static int n,m;
static int[] a = new int[N];
//判断能否做最小的最大值
static boolean check(int x){
int sum = 0;//记录当前段和
int cnt = 1;//记录段数,最小也为1
for (int i = 1; i <= n; i++) {
if(a[i] > x) return false;
if(sum + a[i] <= x){
//当前段还可以继续加
sum = sum + a[i];
}else{
sum = a[i];//另开一段
cnt++;//段数加一
}
}
return cnt <= m;//只有cnt大于m,才说明这个x不是最小的最大值
//可以等于是因为元素可能较小,m给的大。
}
//主逻辑函数
static void solve(){
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
m = sc.nextInt();
for (int i = 1; i <= n; i++) {
a[i] = sc.nextInt();
}
//二分最小的最大值
//随着i的增大,cnt越来越小。
int l = 1,r = (int)1e9;
while(l < r){
int mid = l + r >> 1;
if(check(mid)) r = mid;
else l = mid + 1;
}
System.out.println(l);
}
public static void main(String[] args) {
solve();
}
}
练习题
1.求阶乘–蓝桥2145
求末尾恰好有几个0。
思路:
只有2*5才可以得到10。
而且N!中,2的数量一定大于5的数量,所以要统计有多少个5,就可以知道有多少个0。
统计5就统计5的倍数,而对于一些特殊的数25,125,里面不止包含1个5。
对于N,只要一直除5,就可以得到5的数量了。这就是本题的第二个难点。
要注意可以二分的点,判断大于小于
要灵活变通,题目已经有了提示:这样的N不存在输出-1,就要注意判断
暴力
import java.util.Scanner;
public class main {
static long k;
//判断
static boolean check(long x){
long count = 0;//统计0的数量
while(x != 0){
count += x / 5;
x = x / 5;
}
if(count == k) return true;
else return false;
}
static void solve(){
Scanner sc = new Scanner(System.in);
k = sc.nextLong();
//枚举n
for (long i = 1; i <= (long)1e18; i++) {
if(check(i)){
System.out.println(i);
return;//结束循环
}
}
System.out.println(-1);//不存在输出-1
}
public static void main(String[] args) {
solve();
}
}
import java.util.Scanner;
public class main {
static long k;
//判断
static long getCount(long x){
long count = 0;//统计0的数量
while(x != 0){
count += x / 5;
x = x / 5;
}
return count;
}
static void solve(){
Scanner sc = new Scanner(System.in);
k = sc.nextLong();
//二分
long l = 1, r = Long.MAX_VALUE - 1;//这里r不减1的话,l+r第一步就溢出了
while (l < r){
long mid = l + r >> 1;
long count = getCount(mid);
if(count >= k) r = mid;
else l = mid + 1;
}
if(getCount(l) == k) System.out.println(l);
else{
System.out.println(-1);//不存在输出-1
}
}
public static void main(String[] args) {
solve();
}
}
搜索
一、回溯法+二进制枚举 要求:省一会
1.递归实现排列型枚举
https://www.lanqiao.cn/problems/19684/learning/
package huisu;
//递归实现排列型枚举
//n 个正整数排成一行后随机打乱顺序,按字典序输出所有不同的方案。
//输入一行,包含一个正整数n
//输出n! 行,每一行为一种方案。字典序较小的先输出。
import java.util.Scanner;
//对于暴力做法,对于一个数就是一次循环,每次循环还要判断数字是否被上一层选中
//每一次的所有数选完并输出后,要让数字变回未被选中状态,进行下一次的选择
public class Test1 {
//记录数字是否被选中
static boolean[] st = new boolean[10];
//用数组记录排列的数据
static int[] path = new int[10];
static int n;
//回溯
static void dfs(int u){//u表示的是这是第几个数的选择,本题我们把数的选择转移到数组中
if(u > n){
//所有的数字选完了,输出
for (int i = 1; i <= n; i++) {
System.out.print(path[i]);
}
//换行
System.out.println();
}
//每一次的选择
for (int i = 1; i <= n; i++) {
//判断这个数是否被选过
if(st[i]) continue;
//没被选就选中它,并记下
st[i] = true;
path[u] = i;
//选择下一个数
dfs(u + 1);
//回溯
st[i] = false;
}
}
//主逻辑函数
public static void solve(){
Scanner sc = new Scanner(System.in);
//输入n
n = sc.nextInt();
//输出
dfs(1);
}
public static void main(String[] args) {
solve();
}
}
package com.mj;
import java.util.*;
public class Main{
//记录数字是否被选中
static boolean[] st = new boolean[10];
//用数组记录排列的数据
static int[] path = new int[10];
static int n;
//
static void dfs(int u) {
if(u > n) {
//递归边界,输出
for(int i = 1;i <= n; i++) {
System.out.print(path[i] + " ");
}
System.out.println();//换行
}
//每一次的选择
for(int i = 1;i <= n; i++) {
//判断是否被选过
if(st[i]) continue;
st[i] = true;//没备选就选
path[u] = i;//记下
dfs(u + 1);//下一格数
st[i] = false;//回溯
}
}
//主逻辑函数
static void solve() {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
//输出dfs
dfs(1);
}
public static void main(String[] args){
solve();
}
}
1.2带分数–蓝桥208
思路,1-9出现且只出现1次,进行9的全排列,把9个数的所有组合排列出来,再把加号和除号放进去,看看哪些符合条件
package com.mj;
import java.util.*;
public class Main{
//记录数字是否被选中
static boolean[] st = new boolean[15];
//用数组记录排列的数据
static int[] path = new int[15];
static int n;
static int ans;
//
static void check(String s) {
for(int i = 1; i <= 7; i++) {//加号可以放置的位置
for(int j = i + 1; j <= 8; j++) {//除以号可以放置的位置
int A = Integer.parseInt(s.substring(0,i));
int B = Integer.parseInt(s.substring(i,j));
int C = Integer.parseInt(s.substring(j));
if (C != 0 && A + (double)B / C == n) {
ans++;
}
}
}
}
static void dfs(int u) {
if(u > 9) {
//递归边界,9个数全排列完毕
StringBuilder sb = new StringBuilder();
for (int i = 1; i <= 9; i++) {
sb.append(path[i]);
}
String s = sb.toString();
check(s);
}
//每一次的选择
for(int i = 1;i <= 9; i++) {
//判断是否被选过
if(st[i]) continue;
st[i] = true;//没备选就选
path[u] = i;//记下
dfs(u + 1);//下一格数
st[i] = false;//回溯
}
}
//主逻辑函数
static void solve() {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
//
dfs(1);
System.out.print(ans);
}
public static void main(String[] args){
solve();
}
}
2.递归实现指数型的枚举
https://www.lanqiao.cn/problems/19685/learning/
import java.util.Scanner;
public class Main {
static int n;
static int[] st = new int[20];//记录选与不选
//递归函数
static void dfs(int u){
if(u > n){
//数都选与不选完了
//输出
for (int i = 1; i <= n; i++) {
if(st[i] == 1) System.out.print(i + " ");
}
//换行
System.out.println();
//返回
return;
}
for (int i = 0; i <= 1; i++) {
//0表示不选,1表示选
st[u] = i;
dfs(u + 1);//下一个数字
}
}
//主逻辑函数
public static void solve(){
Scanner sc = new Scanner(System.in);
//输入n
n = sc.nextInt();
dfs(1);
}
public static void main(String[] args) {
solve();
}
}
package huisu;
import java.util.Scanner;
public class Test2 {
static int n;
static int[] st = new int[20];//记录选与不选
//递归函数
static void dfs(int u){
if(u > n){
//数都选与不选完了
//输出
for (int i = 1; i <= n; i++) {
if(st[i] == 1) System.out.print(i + " ");
}
//换行
System.out.println();
//返回
return;
}
/*for (int i = 0; i <= 1; i++) {
//0表示不选,1表示选
st[u] = i;
dfs(u + 1);//下一个数字
}*/
st[u] = 0;
dfs(u + 1);
st[u] = 1;
dfs(u + 1);
}
//主逻辑函数
public static void solve(){
Scanner sc = new Scanner(System.in);
//输入n
n = sc.nextInt();
dfs(1);
}
public static void main(String[] args) {
solve();
}
}
3.递归实现组合型枚举
https://www.lanqiao.cn/problems/19686/learning/
思路:每个数选与不选,只要等于3了,就输出结果;
//
额,我的思路根本写不出题解,也不是很对。
一定要注意审题和看样例了解题目啊
输出字典序,并且数字不重复!
所以就按照m个数字来,每次每个数字都循环(1->n),但由于不重复且递增,所以对于下一个数,是dfs(u + 1,i + 1)
import java.util.Scanner;
public class Main {
static int n,m;
static int[] st = new int[20];//记录选与不选
//递归函数
static void dfs(int u,int start){
if(u > m){
for (int i = 1; i <= m; i++) {
System.out.print(st[i] + " ");
}
System.out.println();
return;
}
for (int i = start; i <= n; i++) {
st[u] = i;
dfs(u + 1,i + 1);// 下一个数,保证是递增不重复的
}
}
//主逻辑函数
public static void solve(){
Scanner sc = new Scanner(System.in);
//输入n,m
n = sc.nextInt();
m = sc.nextInt();
dfs(1,1);//从第一个位置,1开始
}
public static void main(String[] args) {
solve();
}
}
4.蛋糕的美味值
https://www.lanqiao.cn/problems/8664/learning
思路:其实和第二题差不多,每次都是选和不选,到最后u>n时候,就循环计算sum,判断是否大于k,不是的话判断这些小于k的sum中最大的值。
package huisu;
import java.util.Scanner;
public class Test5 {
static int n,k;
//记录蛋糕
static int[] taste = new int[25];
//记录选择
static int[] st = new int[25];
static int Max = 0;
//递归计算蛋糕的最大美味值
public static void dfs(int u){
if(u > n){
int sum = 0;
for (int i = 1; i <= n; i++) {
if(st[i] == 1)
sum = sum + taste[i];
}
if(sum < k){
Max = Math.max(Max,sum);
}
return;
}
for (int i = 0; i <= 1; i++) {
st[u] = i;
dfs(u + 1);
}
}
public static void solve(){
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
k = sc.nextInt();
for (int i = 1; i <= n; i++) {
taste[i] = sc.nextInt();
}
dfs(1);
System.out.println(Max);
}
public static void main(String[] args) {
solve();
}
}
5.N皇后问题
https://www.lanqiao.cn/problems/1508/learning/
难点在于如何使得不在同一排,同一列,与棋盘边框成45度角的斜线。
把棋盘二维问题转换为一维数组的问题
对每列进行查找,找到位置后进行下一列的选择。就避免了二维问题。
这个与棋盘边框成45度角的斜线。就要找规律了。。。
要注意数值范围
package huisu;
import java.util.Scanner;
public class Test6 {
static int n;
static int ans;
static boolean[] col = new boolean[30];
static boolean[] zhu = new boolean[30];
static boolean[] fu = new boolean[30];
public static void dfs(int u){
if(u > n){
ans++;
return;
}
//u表示第u行
//循环的i表示第几列
for (int i = 1; i <= n; i++) {
//首先判断
//不在同一排,同一列,与棋盘边框成45度角的斜线。
if(col[i] || zhu[n - i + u] || fu[u + i]) continue;
//选择
col[i] = true;
zhu[n - i + u] = true;
fu[u + i] = true;
dfs(u + 1);//下一行
//回溯,释放位置
col[i] = false;
zhu[n - i + u] = false;
fu[u + i] = false;
}
}
public static void solve(){
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
dfs(1);//从第一行开始
System.out.println(ans);
}
public static void main(String[] args) {
solve();
}
}
6.五子棋对弈
https://www.lanqiao.cn/problems/19694/learning/
思路是对白棋进行递归判断
每次对每个格子都是选与不选,直至25个格子全部选完。
选完之后判断白棋的个数是不是13个
是的话再判断是否为平局。
平局就是排除掉横竖对角线,主副对角线的总和为0或5的。
package huisu;
public class Test7 {
static int[] st = new int[30];
static int[][] qi = new int[10][10];
static int ans;
//判断平局
public static boolean check(){
int k = 1;
//1表示白棋,0表示黑棋
//那么横竖和对角线的sum不能是0或者5
//放到二维数组中更好计算判断
for (int i = 1; i <= 5; i++) {
for (int j = 1; j <= 5; j++) {
qi[i][j] = st[k];
k++;
}
}
//判断横竖
int[] row = new int[6];
int[] col = new int[6];
for (int i = 1; i <= 5; i++) {
for (int j = 1; j <= 5; j++) {
row[i] += qi[i][j];
col[i] += qi[j][i];
}
}
for (int i = 1; i <= 5; i++) {
if(row[i] == 5 || row[i] == 0 || col[i] == 5 || col[i] == 0)
return false;
}
//判断主副对角线
int zhu = 0;
int fu = 0;
for (int i = 1; i <= 5; i++) {
zhu += qi[i][i];
fu += qi[i][6 - i];
}
if(zhu == 5 || zhu == 0 || fu == 5 || fu == 0)
return false;
//都判断完
return true;
}
//递归函数
public static void dfs(int u){
if (u > 25){
int count = 0;
//判断白棋是否有13颗
for (int i = 1; i <= 25; i++) {
if(st[i] == 1)
count++;
}
if(count == 13){
//有13颗,那黑棋12颗
//接下来就判断是否平局
if(check())
ans++;
}
//返回
return;
}
//u表示每一格子
for (int i = 0; i <= 1; i++) {
st[u] = i;
dfs(u + 1);//下一格
}
}
public static void solve(){
dfs(1);//对白棋进行选择
System.out.println(ans);
}
public static void main(String[] args) {
solve();
}
}
在主副对角线的计算那里
/*for (int i = 1; i <= 5; i++) {
for (int j = 1; j <= 5; j++) {
zhu += qi[i][i];
fu += qi[i][5 - i + 1];
}
}*/
刚开始这么计算,是错误的,我重复计算了。呃呃呃。
二维数组还需要学。。。。
7.宝塔
https://www.lanqiao.cn/problems/20111/learning/
8.数字接龙
https://www.lanqiao.cn/problems/19712/learning/
java知识点
1.Java数组默认值
在Java中,当数组被定义但未被显式初始化时,数组元素会自动赋予默认值。不同数据类型的数组有不同的默认值。
整型数组默认值
对于整型数组(如byte、short、int、long),默认值为0。例如:
int[] intArray = new int[5];
System.out.println(intArray[0]); // 输出:0
无论是byte、short、int还是long,其默认值均为0
2.最大值:
Long.MAX_VALUE
在二分中可能会使用,计算mid时要注意溢出问题,l=1的话一般要减一操作。
蓝桥杯就这样子了,一边学一边玩了一个月,感觉明天就是寄了。