一、问题引入
一列挂有 节车厢(编号从 1 到 )的货运列车途径 个车站,计划在行车途中将各节车厢停放在不同的车站。假设 个车站的编号从 1 到 ,货运列车按照从第 站到第 1 站的顺序经过这些车站,且将与车站编号相同的车厢卸下。
货运列车的各节车厢以随机顺序入轨,为方便列车在各个车站卸掉相应的车厢,须重排这些车厢,使得各车厢从前往后依次编号为 1 到 ,这样在每个车站只需卸掉当前最后一节车厢即可。
车厢重排可通过转轨站完成。一个转轨站包含一个入轨,一个出轨和 个位于入轨和出轨之间的缓冲轨。缓冲轨用于存储尚未确定输出次序的车厢。重排车厢的规则包含如下三条:
- 一个车厢从入轨移至出轨或缓冲轨;
- 一个车厢只有在其编号恰是下一个待输出编号时,可移到出轨;
- 一个车厢移到某个缓冲轨,仅当其编号大于该缓冲轨中队尾车厢的编号,若多个缓冲轨满足这一条件,则选择队尾车厢编号最大的,否则选择一个空缓冲轨,若无空缓冲轨则无法重排。
请你编写程序实现这个重排算法。
输入格式:
输入在第一行中给出两个正整数n和k,均不超过 100,分别为车厢数量和缓冲轨数量。第二行按入轨顺序给出n节车厢的编号,数字间以空格分隔。
输出格式:
按照车厢进入出轨的顺序,输出每节车厢在入轨时的位序(从 0 开始),每个数字占一行。若无法重排,则在一行中输出信息
错误:任务不可能完成。
输入样例 1:
9 3
5 8 1 7 4 2 9 6 3
输出样例 1:
2
5
8
4
0
7
3
1
6
输入样例 2:
9 2
5 8 1 7 4 2 9 6 3
输出样例 2:
错误:任务不可能完成。
二、解决思路
算法步骤
- 初始化:创建k个缓冲轨(可以用栈或双端队列表示)
- 处理每节车厢:
◦ 如果当前车厢是下一个需要的编号,直接输出
◦ 否则,尝试将车厢放入合适的缓冲轨 - 检查缓冲轨:每次直接输出车厢后,检查缓冲轨顶部是否有下一个需要的车厢
- 处理剩余车厢:所有入轨车厢处理完后,检查缓冲轨中剩余的车厢是否能按顺序输出
关键点
• 缓冲轨选择策略:当需要将车厢放入缓冲轨时,优先选择队尾编号最大的缓冲轨,这样可以保持缓冲轨的降序排列,为后续车厢提供更多选择空间
• 及时检查缓冲轨:每次输出车厢后立即检查缓冲轨,确保不会错过可以输出的车厢
三、代码实现
import java.util.*;
public class Main {
public static String canRearrange(int n, int k, List<Integer> carriages) {
// 初始化缓冲轨
List<Deque<Integer>> buffers = new ArrayList<>();
for (int i = 0; i < k; i++) {
buffers.add(new ArrayDeque<>());
}
int nextCarriage = 1; // 下一个待输出的车厢编号
List<Integer> output = new ArrayList<>(); // 记录车厢进入出轨的顺序
for (int i = 0; i < carriages.size(); i++) {
int carriage = carriages.get(i);
// 如果当前车厢是下一个待输出的车厢,直接输出
if (carriage == nextCarriage) {
output.add(i);
nextCarriage++;
// 检查缓冲轨中是否有可以输出的车厢
while (true) {
boolean moved = false;
for (Deque<Integer> buffer : buffers) {
if (!buffer.isEmpty() && buffer.peekFirst() == nextCarriage) {
output.add(carriages.indexOf(buffer.pollFirst()));
nextCarriage++;
moved = true;
}
}
if (!moved) {
break;
}
}
} else {
// 尝试将车厢放入缓冲轨
int validBuffer = -1;
int maxTail = -1;
for (int j = 0; j < buffers.size(); j++) {
Deque<Integer> buffer = buffers.get(j);
if (buffer.isEmpty() || buffer.peekLast() < carriage) {
if (!buffer.isEmpty() && buffer.peekLast() > maxTail) {
validBuffer = j;
maxTail = buffer.peekLast();
} else if (buffer.isEmpty() && validBuffer == -1) {
validBuffer = j;
}
}
}
if (validBuffer != -1) {
buffers.get(validBuffer).addLast(carriage);
} else {
return "错误:任务不可能完成。";
}
}
}
// 处理缓冲轨中剩余的车厢
while (nextCarriage <= n) {
boolean moved = false;
for (Deque<Integer> buffer : buffers) {
if (!buffer.isEmpty() && buffer.peekFirst() == nextCarriage) {
output.add(carriages.indexOf(buffer.pollFirst()));
nextCarriage++;
moved = true;
}
}
if (!moved) {
return "错误:任务不可能完成。";
}
}
// 将输出顺序转换为字符串
StringBuilder result = new StringBuilder();
for (int idx : output) {
result.append(idx).append("\n");
}
return result.toString().trim();
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// 读取输入
int n = scanner.nextInt();
int k = scanner.nextInt();
List<Integer> carriages = new ArrayList<>();
for (int i = 0; i < n; i++) {
carriages.add(scanner.nextInt());
}
// 调用函数并输出结果
String result = canRearrange(n, k, carriages);
System.out.println(result);
scanner.close();
}
}
四、总结
车厢重排问题是一个经典的栈排序应用场景,考察对缓冲轨(栈结构)的灵活运用能力。该问题的核心在于如何利用有限的缓冲轨资源,将随机入轨的车厢重新排列成有序序列。通过分析,我们得出以下关键要点:
首先,算法采用贪心策略处理缓冲轨选择,优先将车厢放入队尾编号最大的缓冲轨。这种策略能最大化保留后续车厢的放置选择空间,是解决问题的关键所在。当车厢编号恰好是下一个待输出编号时,立即输出并触发缓冲轨的连锁检查机制,确保不会错过任何可输出的车厢。
其次,算法的时间复杂度为O(n²),因为每个车厢可能需要遍历所有缓冲轨进行放置或检查。空间复杂度为O(n),由缓冲轨的存储需求决定。值得注意的是,缓冲轨数量k直接影响问题可解性,当k不足时(如样例2中k=2无法处理峰值需求),算法会及时终止并返回错误信息。