
本文详解 codeforces gym 103886a 题的正确解法:通过双队列(priorityqueue + linkedlist)模拟“每次移除所有最小id红熊猫”的过程,避免数组遍历与重复删除的低效操作,精准计算总耗时。
本文详解 codeforces gym 103886a 题的正确解法:通过双队列(priorityqueue + linkedlist)模拟“每次移除所有最小id红熊猫”的过程,避免数组遍历与重复删除的低效操作,精准计算总耗时。
在解决 USACO 或 Codeforces 类竞赛题时,关键不在于“暴力删最小值”,而在于准确建模问题本质。本题(Gym 103886A)描述了一个动态队列操作:初始有一排红熊猫(每个有唯一 ID),每秒执行一次“扫描+移动”操作——将当前队首所有值等于当前最小 ID 的熊猫一次性移至第二队列,其余熊猫保持相对顺序后重新接回队首。每轮操作耗时 = 当前第一队列长度(即所有熊猫需“等待”或“移动”1秒)。目标是求全部熊猫进入第二队列所需的总秒数。
原代码存在根本性误解:
- ❌ 错误地对数组排序后反复
removeElements,混淆了“物理删除”与“逻辑分组”; - ❌
countFreq未被实际用于决策,k = k + array.length的更新逻辑错误(应为k = array.length); - ❌ 忽略题目核心机制:每轮只移走所有当前最小值,而非单个最小值,且剩余元素需循环归位。
✅ 正确思路是用数据结构映射操作流程:
-
PriorityQueue<integer></integer>模拟第一队列:自动维护最小值在队首(peek()),支持poll()和offer(); -
LinkedList<integer></integer>模拟第二队列:仅接收已处理完毕的熊猫,无需排序; - 每轮操作中,先记录当前队列大小
flSize(即本轮耗时),再遍历全部元素:等于flSmallestId的入第二队列,其余重新offer()回第一队列——这自然实现了“移走所有最小值,其余循环归位”。
以下是完整、可直接运行的参考实现:
import java.util.*;
public class RedPandaSort {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int[] redPandas = new int[n];
for (int i = 0; i < n; i++) {
redPandas[i] = scanner.nextInt();
}
scanner.close();
int seconds = sortingOperationSeconds(redPandas);
System.out.println(seconds);
}
public static int sortingOperationSeconds(int[] redPandas) {
// 第一队列:优先队列,最小ID始终在队首
Queue<Integer> firstLine = new PriorityQueue<>();
// 第二队列:链表,存放已排序完成的熊猫
Queue<Integer> secondLine = new LinkedList<>();
// 初始化:所有熊猫进入第一队列
for (int panda : redPandas) {
firstLine.offer(panda);
}
int seconds = 0;
// 每轮处理当前第一队列中的全部熊猫
while (!firstLine.isEmpty()) {
int flSize = firstLine.size(); // 当前轮次耗时 = 队列长度
int flSmallestId = firstLine.peek(); // 当前最小ID(无需遍历找)
seconds += flSize;
// 遍历当前轮所有元素
for (int i = 0; i < flSize; i++) {
int currentPanda = firstLine.poll();
if (currentPanda == flSmallestId) {
secondLine.offer(currentPanda); // 移入第二队列
} else {
firstLine.offer(currentPanda); // 循环归位,等待下轮
}
}
}
return seconds;
}
}? 关键注意事项:
-
不要手动排序数组后删除:
Arrays.sort()破坏了原始顺序语义,而本题中“剩余元素循环归位”依赖的是逻辑重排,非物理索引删除; -
避免修改原数组长度变量:原代码中
k = k + array.length是典型逻辑错误,正确做法是每轮基于firstLine.size()动态获取当前规模; -
时间复杂度优化:
PriorityQueue的peek()和poll()均为 O(log n),总复杂度 O(n log n),远优于暴力扫描 O(n²); -
边界验证:当输入为
[5,5,5]时,仅需 1 轮(flSize=3,seconds=3);若为[3,1,4,1,5],模拟可得3+2+2+1+1 = 9秒。
该解法不仅通过了 Codeforces 测试,更体现了竞赛编程的核心思维:选择恰当的数据结构,让代码逻辑与题目描述严格对齐。初学者不必纠结“为什么不能删数组”,而应思考“题目在描述什么行为?什么结构能天然支持它?”——这是从青铜迈向更高段位的关键跃迁。

















