对于在中国大学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;
        }
    }
}

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐