P1271 【深基9.例1】选举学生会
解题思路
- 使用例如冒泡、选择、插入、快速、合并等排序算法只能满足1、2、5三个测试用例,3、5测试用例会超时。(其实看到题目给出的数据就知道排序算法效率起码要在nlogn级别才可能不会超时)
在处理特别大的测试用例时,如果标准排序算法可能超时,可以考虑使用桶排序(Bucket Sort)或计数排序(Counting Sort),尤其是当输入数据的范围相对较小且固定时。这在这个问题中尤为适用,因为候选人的编号是固定的(从 1 到 n)。
计数排序是一种适用于范围有限的整数排序算法。它的时间复杂度为 O(m+n)O(m + n)O(m+n),在这种场景下会比基于比较的排序算法(如 Timsort)的 O(mlogm)O(m \log m)O(mlogm) 更加高效。
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
// 读取 n 和 m
int n = input.nextInt();
int m = input.nextInt();
// 计数数组
int[] count = new int[n + 1];
// 读取选票编号并计数
for (int i = 0; i < m; i++) {
int vote = input.nextInt();
count[vote]++;
}
input.close();
// 输出排序后的结果
StringBuilder result = new StringBuilder();
for (int i = 1; i <= n; i++) {
while (count[i] > 0) {
result.append(i).append(" ");
count[i]--;
}
}
// 去掉最后一个多余的空格
if (result.length() > 0) {
result.setLength(result.length() - 1);
}
System.out.println(result);
}
}
P1177 【模板】排序
解题思路
内置函数排序
import java.util.Arrays;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int n = input.nextInt();
int[] arr = new int[n];
for (int i = 0; i < n; i++) {
arr[i] = input.nextInt();
}
Arrays.sort(arr);
for (int i = 0; i < n; i++) {
System.out.print(arr[i] + " ");
}
}
}
P1923 【深基9.例4】求第 k 小的数
这题我不好评价。如果你和我一样就傻愣愣的用java直接写这道题,一星期都是60。用java,如果你不用io流、StreamTokenizer的类后两个测试用例包过不了的,就算你用线段树时间满足了空间也不满足的。
解题思路
- 题目要求用分治算法:考虑快速排序和合并排序。
- 输入是n和k,以及n个待排序的数字。
- 使用io流快读数据。
- 最后输出arr[k]。
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.StreamTokenizer;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StreamTokenizer st = new StreamTokenizer(br);
// 读取输入的第一个数字n
st.nextToken();
int n = (int) st.nval;
// 读取输入的第二个数字k
st.nextToken();
int k = (int) st.nval;
// 创建一个长度为n的数组
int[] nums = new int[n];
// 读取输入的n个数字,存入数组
for (int i = 0; i < n; i++) {
st.nextToken();
nums[i] = (int) st.nval;
}
// 输出第k大的数字
System.out.println(quickSelect(nums, 0, n - 1, k));
}
public static int quickSelect(int[] arr, int left, int right, int k) {
while (left <= right) {
// 随机选择一个数字作为基准
int pivotIndex = partition(arr, left, right);
// 如果基准的下标等于k,则返回基准
if (pivotIndex == k) {
return arr[pivotIndex];
} else if (pivotIndex < k) {
// 如果基准的下标小于k,则说明第k大的数字在基准的右边
left = pivotIndex + 1;
} else {
// 如果基准的下标大于k,则说明第k大的数字在基准的左边
right = pivotIndex - 1;
}
}
return -1;
}
public static int partition(int[] arr, int left, int right) {
// 选择右边的数字作为基准
int pivot = arr[right];
int i = left;
// 将小于基准的数字放到基准的左边
for (int j = left; j < right; j++) {
if (arr[j] <= pivot) {
swap(arr, i, j);
i++;
}
}
// 将基准放到正确的位置
swap(arr, i, right);
return i;
}
public static void swap(int[] arr, int i, int j) {
// 交换数组中的两个数字
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
P1059 [NOIP2006 普及组] 明明的随机数
解题思路
- 输入读取: 首先读入数据,然后使用内置函数排序,然后设计remove方法对有序数组去重。
- 去重算法:
remove
方法负责从有序数组中去除重复元素,并返回去重后的元素个数。
- 使用
Count
变量记录唯一元素的个数。初始值为1,因为第一个元素总是唯一的。- 从数组的第二个元素开始遍历,如果当前元素与
Count - 1
位置的元素不同,则将当前元素复制到Count
位置,并增加Count
。- 结果输出: 时间复杂度是O(n),因为它只需要一次遍历数组。
import java.util.Arrays;
import java.util.Scanner;
/**
* @Author HeShen.
* @Date 2024/4/13 19:49
*/
public class Main {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int n = input.nextInt();
int[] arr = new int[n];
for (int i = 0; i < n; i++) {
arr[i] = input.nextInt();
}
Arrays.sort(arr);
int ans = remove(arr);
System.out.println(ans);
for (int i = 0; i < ans; i++) {
System.out.print(arr[i] + " ");
}
}
public static int remove(int[] nums) {
int count = 1;
for (int i = 1; i < nums.length; i++) {
if (nums[i] != nums[count - 1]) {
nums[count] = nums[i];
count++;
}
}
return count;
}
}
P1093 [NOIP2007 普及组] 奖学金
解题思路
Student类: 定义一个Student类来存储每个学生的信息,包括学号、各科成绩和总分。这个类实现了Comparable接口,以便我们可以自定义排序规则。
compareTo方法: 在compareTo方法中定义排序规则:
- 先按总分降序排序。
- 如果总分相同,再按语文成绩降序排序。
- 如果总分和语文成绩都相同,再按学号升序排序。
读取输入: 使用Scanner读取输入的学生数量和每个学生的成绩。将每个学生的数据存储到Student对象中,并添加到students列表。
排序: 使用Collections.sort对学生列表进行排序,排序后学生列表中前五个元素即为我们需要的前五名学生。
输出结果: 输出前五名学生的学号和总分。
import java.util.*;
/**
* @Author HeShen.
* @Date 2024/4/13 19:49
*/
public class Main {
static class Student implements Comparable<Student>{
int num;
int ch, ma, en;
int sum;
public Student(int num, int ch, int ma, int en){
this.num = num;
this.ch = ch;
this.ma = ma;
this.en = en;
this.sum = ch + ma + en;
}
@Override
public int compareTo(Student o) {
if (this.sum != o.sum) {
return o.sum - this.sum;
}else if (this.ch != o.ch) {
return o.ch - this.ch;
}else {
return this.num - o.num;
}
}
}
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int n = input.nextInt();
List<Student> students = new ArrayList<>();
for (int i = 1; i <= n; i++) {
int ch = input.nextInt();
int ma = input.nextInt();
int en = input.nextInt();
students.add(new Student(i, ch, ma, en));
}
Collections.sort(students);
for (int i = 0; i < 5; i++) {
Student s = students.get(i);
System.out.println(s.num + " " + s.sum);
}
}
}
P1781 宇宙总统
解题思路
java中针对位数较高的数可以使用BigInteger表示。
基本思路是读入数据,然后设置一个max来存储最大值,id存储序号,找到最大值后输出。
import java.math.BigInteger;
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int n = input.nextInt();
int id = 0;
BigInteger[] arr = new BigInteger[n + 1];
BigInteger max = BigInteger.ZERO;
for (int i = 1; i <= n; i++) {
arr[i] = input.nextBigInteger();
if (arr[i].compareTo(max) > 0) {
max = arr[i];
id = i;
}
}
System.out.println(id);
System.out.println(max);
}
}
P2676 [USACO07DEC] Bookshelf B
解题思路
基本思路是读入数据后进行排序,然后从最大的身高开始加,直到S>=B.
读入数据后使用java内置函数排序,需要注意的是,这里我使用了Collections的方法逆序排序,这样方便写while循环。当然也可以不使用逆序排序。
使用逆序排序
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int n = input.nextInt();
int b = input.nextInt();
int count = 0;
int sum = 0;
Integer [] arr = new Integer[n];
for (int i = 0; i < n; i++) {
arr[i] = input.nextInt();
}
Arrays.sort(arr, Collections.reverseOrder());
while (sum < b) {
sum += arr[count];
count++;
}
System.out.println(count);
}
}
不使用逆序排序
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int n = input.nextInt();
int b = input.nextInt();
int count = 0;
int sum = 0;
int [] arr = new int[n];
for (int i = 0; i < n; i++) {
arr[i] = input.nextInt();
}
Arrays.sort(arr);
while (sum < b) {
sum += arr[n - 1];
n--;
count++;
}
System.out.println(count);
}
}
P1116 车厢重组
解题思路
典型的冒泡排序,还是用冒泡排序来做比较符合题意。返回值是交换次数,在执行冒泡排序的时候,交换一次计数加一,最后返回交换次数。
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int n = input.nextInt();
int[] arr = new int[n];
for (int i = 0; i < n; i++) {
arr[i] = input.nextInt();
}
System.out.println(count(arr));
}
public static int count(int[] arr) {
int count = 0;
for (int i = arr.length - 1; i > 0; i--) {
for (int j = 0; j < i; j++) {
if (arr[j] > arr[j + 1]) {
int tem = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tem;
count++;
}
}
}
return count;
}
}
P1152 欢乐的跳
解题思路
输入与初始化:
- 使用
Scanner
读取输入的整数 n,表示序列的长度。- 使用数组
a
存储输入的整数序列,大小设为 1001。- 使用布尔数组
b
来记录绝对差值是否出现过,大小设为 1001。读取输入序列:
- 通过一个循环读取 n 个整数并存入数组
a
中。注意数组从索引1开始存储,因为第一个输入的n就是数组的第一个元素。计算并记录绝对差值:
- 从第二个元素开始,计算相邻两个元素的绝对差值。
- 检查绝对差值是否在合法范围内(小于等于 1000)。
- 如果差值在范围内,将其在数组
b
中标记为true
。检查差值是否齐全:
- 检查数组
b
,判断从 1 到 n-1 的差值是否都出现过。- 如果有任何一个差值没有出现,则输出 "Not jolly" 并返回。
- 如果所有差值都出现了,则输出 "Jolly"。
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int n = input.nextInt();
int[] a = new int[1001]; // 存储输入
boolean[] b = new boolean[1001]; // 存储差值
for (int i = 1; i <= n; i++) {
a[i] = input.nextInt();
}
for (int i = 2; i <= n; i++) {
int abs = Math.abs(a[i] - a[i - 1]);
// 题目给出了n的范围,一定要判断差值是否落在范围内
if (abs <= 1000) {
b[abs] = true; // 标记成出现过
}
}
for (int i = 1; i <= n - 1; i++) {
if (!b[i]) { // 如果b[i]为假,输出Not jolly。有一个不满足即可退出。
System.out.println("Not jolly");
return;
}
}
System.out.println("Jolly"); // 如果没有,输出“Jolly”
}
}
P1068 [NOIP2009 普及组] 分数线划定
解题思路
该题类似 P1093 [NOIP2007 普及组] 奖学金。
- Participant类: 定义一个Participant类来存储每个志愿者的信息,包括报名号、分数。
- 读取输入数据:使用
Scanner
类读取输入,用List存储对象。- 计算面试分数线:计算 m×1.5 并向下取整得到排名位置,然后从所有选手中获取该排名对应的分数。
- 筛选选手:将所有符合条件的选手筛选出来。
- 排序:对选手进行排序,首先按成绩从高到低排序,如果成绩相同,则按报名号从小到大排序。
- 输出结果:按照格式要求输出结果。
import java.util.*;
public class Main {
static class Participant {
int id;
int score;
Participant(int id, int score) {
this.id = id;
this.score = score;
}
}
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int n = input.nextInt();
int m = input.nextInt();
List<Participant> participants = new ArrayList<>();
for (int i = 0; i < n; i++) {
int id = input.nextInt();
int score = input.nextInt();
participants.add(new Participant(id, score));
}
// 优先按照分数排序,如果分数相同,按照id排序
participants.sort((p1, p2) -> {
if (p1.score != p2.score) {
return p2.score - p1.score;
} else {
return p1.id - p2.id;
}
});
// 与录取人数向下取整
int cutoffRank = (int) Math.floor(m * 1.5);
// 确定面试分数线
int interviewScoreLine = participants.get(cutoffRank - 1).score;
// 添加所有达到面试分数线的参与者
List<Participant> interviewed = new ArrayList<>();
for (Participant p : participants) {
if (p.score >= interviewScoreLine) {
interviewed.add(p);
}
}
// 输出
System.out.println(interviewScoreLine + " " + interviewed.size());
for (Participant p : interviewed) {
System.out.println(p.id + " " + p.score);
}
input.close();
}
}
P5143 攀爬者
解题思路
- 输入读取和存储:首先读取所有点的坐标,并存储在一个列表中。
- 排序:根据高度 z 对所有点进行排序,确保每个点都比前一个点高。
- 计算总距离:依次计算相邻点之间的欧几里得距离,并累加这些距离得到总距离。
- 输出结果:最后输出总距离,并保留三位小数。
实现细节
- 数据结构:使用一个类
Point
来存储每个点的坐标。- 排序:利用java的内置排序功能,对点按高度
z
进行排序。- 距离计算:编写一个函数计算两个点之间的欧几里得距离。
- 精度处理:使用java的字符串格式化功能来保留三位小数。
特别是这个精度处理,如果你使用其他的方式,例如使用String format函数再转double会报错,使用iBigDecimal和RoundingMode保留3位小数仍然会报错。直接使用printf不会报错。(别问我怎么知道的)
import java.util.*;
public class Main {
static class Point {
int x;
int y;
int z;
public Point(int x, int y, int z) {
this.x = x;
this.y = y;
this.z = z;
}
}
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int n = input.nextInt();
List<Point> mountains = new ArrayList<>();
for (int i = 0; i < n; i++) {
int x = input.nextInt();
int y = input.nextInt();
int z = input.nextInt();
mountains.add(new Point(x, y, z));
}
mountains.sort(Comparator.comparingInt(a -> a.z));
double sum = 0.0;
for (int i = 0; i < n - 1; i++) {
sum += distance(mountains.get(i), mountains.get(i + 1));
}
System.out.printf("%.3f%n", sum);
}
public static double distance(Point a, Point b) {
return Math.sqrt(Math.pow(a.x - b.x, 2) + Math.pow(a.y - b.y, 2) + Math.pow(a.z - b.z, 2));
}
}
P1104 生日
解题思路
大致代码结构类似 P1068 [NOIP2009 普及组] 分数线划定。
具体操作如下:
- Students类: 定义一个Students类来存储每个人的姓名和生日以及序号。
- 读取输入数据:使用
Scanner
类读取输入,用List存储对象。- 根据出生日期排序:定义a,b两个对象,如果年月日不相等,年月日从小到大排序,如果年月日都相等,根据num从小到大排序。
- 输出结果:按照格式要求输出结果。
import java.util.*;
public class Main {
static class Students {
String s;
int y;
int m;
int d;
int num;
public Students(String s, int y, int m, int d, int num) {
this.s = s;
this.y = y;
this.m = m;
this.d = d;
this.num = num;
}
}
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int n = input.nextInt();
List<Students> students = new ArrayList<>();
for (int i = 0; i < n; i++) {
String s = input.next();
int y = input.nextInt();
int m = input.nextInt();
int d = input.nextInt();
students.add(new Students(s, y, m, d, i));
}
students.sort((a, b) -> {
if (a.y != b.y) {
return a.y - b.y;
}else if (a.m != b.m) {
return a.m - b.m;
}else if (a.d != b.d) {
return a.d - b.d;
}else {
return b.num - a.num;
}
});
for (Students s : students) {
System.out.println(s.s);
}
}
}
P1012 [NOIP1998 提高组] 拼数
解题思路
- 输入读取:读取输入的数字个数 n 和数字列表。
- 自定义排序:定义一种排序规则,基于字符串的拼接结果进行比较。例如,对于两个数 a 和 b,如果拼接 ab 大于拼接 ba,则 a 应该排在 b 前面。
- 排序操作:根据自定义的比较规则,对所有数字进行排序。
- 拼接结果:将排序后的数字依次拼接起来,得到最终结果。
- 输出结果:输出拼接后的最大整数。
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int n = input.nextInt();
String[] numbers = new String[n];
for (int i = 0; i < n; i++) {
numbers[i] = input.next();
}
// 比较a和b拼接谁更大,降序排列
Arrays.sort(numbers, (a, b) -> {
String order1 = a + b;
String order2 = b + a;
return order2.compareTo(order1);
});
StringBuilder result = new StringBuilder();
for (String number : numbers) {
result.append(number);
}
System.out.println(result);
input.close();
}
}