PTA团体程序设计天梯赛 L2-027 名人堂与代金券 (Java)(满分不超时)
·
对于在中国大学MOOC(http://www.icourse163.org/ )学习“数据结构”课程的学生,想要获得一张合格证书,总评成绩必须达到 60 分及以上,并且有另加福利:总评分在 [G, 100] 区间内者,可以得到 50 元 PAT 代金券;在 [60, G) 区间内者,可以得到 20 元PAT代金券。全国考点通用,一年有效。同时任课老师还会把总评成绩前 K 名的学生列入课程“名人堂”。本题就请你编写程序,帮助老师列出名人堂的学生,并统计一共发出了面值多少元的 PAT 代金券。
输入格式:
输入在第一行给出 3 个整数,分别是 N(不超过 10 000 的正整数,为学生总数)、G(在 (60,100) 区间内的整数,为题面中描述的代金券等级分界线)、K(不超过 100 且不超过 N 的正整数,为进入名人堂的最低名次)。接下来 N 行,每行给出一位学生的账号(长度不超过15位、不带空格的字符串)和总评成绩(区间 [0, 100] 内的整数),其间以空格分隔。题目保证没有重复的账号。
输出格式:
首先在一行中输出发出的 PAT 代金券的总面值。然后按总评成绩非升序输出进入名人堂的学生的名次、账号和成绩,其间以 1 个空格分隔。需要注意的是:成绩相同的学生享有并列的排名,排名并列时,按账号的字母序升序输出。
输入样例:
10 80 5
cy@zju.edu.cn 78
cy@pat-edu.com 87
1001@qq.com 65
uh-oh@163.com 96
test@126.com 39
anyone@qq.com 87
zoe@mit.edu 80
jack@ucla.edu 88
bob@cmu.edu 80
ken@163.com 70
输出样例:
360
1 uh-oh@163.com 96
2 jack@ucla.edu 88
3 anyone@qq.com 87
3 cy@pat-edu.com 87
5 bob@cmu.edu 80
5 zoe@mit.edu 80
代码长度限制
16 KB
时间限制
150 ms
内存限制
64 MB
栈限制
8192 KB
import java.util.*;
import java.io.*;
/**
* 【L2-027 名人堂与代金券】
* * [核心优化策略]
* 1. 索引排序:
* 不直接对对象进行排序,而是维护一个 Integer[] 索引数组。排序时只交换 4 字节的索引,
* 数据访问通过索引定位到原始数组(scores, accounts)。
* 2. 规避 String 对象:
* String 对象的创建和 compareTo 方法涉及字符编码解析。改用 byte[] 存储账号,
* 并手动实现字节级对比。
* 3. 原始字节输出:
* 直接将 byte[] 写入输出流
*/
public class Main {
// 静态数组存储,确保存储空间的连续性
static byte[][] accounts; // 存储原始账号字节
static int[] scores; // 存储学生成绩
static Integer[] indices; // 存储排序索引
public static void main(String[] args) throws IOException {
FastReader fr = new FastReader(System.in);
int n = fr.nextInt();
int g = fr.nextInt();
int k = fr.nextInt();
accounts = new byte[n][];
scores = new int[n];
indices = new Integer[n];
long totalVoucher = 0;
// --- 1. 数据采集与初步统计 ---
for (int i = 0; i < n; i++) {
accounts[i] = fr.nextAsBytes(); // 字节级读取,规避 String 内存开销
scores[i] = fr.nextInt();
indices[i] = i; // 初始化索引,指向当前学生
// 累计代金券总额
if (scores[i] >= g) totalVoucher += 50;
else if (scores[i] >= 60) totalVoucher += 20;
}
// --- 2. 索引排序逻辑 ---
// 仅对索引进行重排,根据关联数组中的成绩和账号进行权值判定
Arrays.sort(indices, (a, b) -> {
// 第一关键字:成绩降序
if (scores[a] != scores[b]) return scores[b] - scores[a];
// 第二关键字:账号字节序升序
return compareBytes(accounts[a], accounts[b]);
});
// --- 3. 格式化输出 ---
PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out), 1 << 16));
out.println(totalVoucher);
int rank = 1; // 维护当前排名
for (int i = 0; i < n; i++) {
int idx = indices[i];
// 处理并列排名:若当前学生成绩低于前一名,排名更新为当前已遍历的总人数
if (i > 0 && scores[idx] != scores[indices[i - 1]]) {
rank = i + 1;
}
// 名次超出 K 名(名人堂范围)则终止输出
if (rank > k) break;
out.print(rank);
out.print(' ');
// 核心提速:直接将原始字节写入缓冲区,不经过字符集编码
for (byte b : accounts[idx]) out.write(b);
out.print(' ');
out.println(scores[idx]);
}
out.flush();
out.close();
}
/**
* 手动实现的字节级字典序对比
* 效果等同于 String.compareTo,但省去了对象封装和编码检测
*/
static int compareBytes(byte[] a, byte[] b) {
int len = Math.min(a.length, b.length);
for (int i = 0; i < len; i++) {
// 对比相同位置的字节码
if (a[i] != b[i]) return (a[i] & 0xFF) - (b[i] & 0xFF);
}
return a.length - b.length;
}
/**
* 字节流读取器
*/
static class FastReader {
private final InputStream in;
private final byte[] buf = new byte[1 << 16];
private int ptr = 0, len = 0;
public FastReader(InputStream in) { this.in = in; }
private int read() throws IOException {
if (ptr == len) {
len = in.read(buf);
ptr = 0;
if (len <= 0) return -1;
}
return buf[ptr++];
}
// 将输入的账号直接作为字节数组返回,不触发 String 构造
public byte[] nextAsBytes() throws IOException {
int c = read();
while (c >= 0 && c <= 32) c = read();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
while (c > 32) {
baos.write(c);
c = read();
}
return baos.toByteArray();
}
public int nextInt() throws IOException {
int c = read();
while (c >= 0 && c <= 32) c = read();
int res = 0;
while (c > 32) {
res = res * 10 + (c - '0');
c = read();
}
return res;
}
}
}

更多推荐




所有评论(0)