用C语言手把手教你搞定PTA数据结构里的‘列车厢调度’题(附完整代码和避坑指南)
用C语言手把手教你搞定PTA数据结构里的‘列车厢调度’题附完整代码和避坑指南当你第一次看到PTA平台上的列车厢调度问题时可能会被那些轨道示意图和操作规则搞得一头雾水。别担心这很正常——很多同学都在这个题目上栽过跟头。本文将从一个过来人的角度带你一步步理解题目本质掌握用栈模拟调度的核心思路最终用C语言实现完整解法。我们不仅会提供可直接提交的代码还会重点讲解那些容易踩坑的细节。1. 彻底理解题目从ASCII图到实际问题首先让我们抛开所有代码专注于理解题目本身。题目描述中那个看似简单的ASCII图其实包含了所有关键信息1 --移动方向 / 3 \ \ 2 --移动方向这张图展示了三条平行轨道1、2、3号以及它们的连接方式。初始状态下所有车厢都停在1号轨道上我们的目标是通过有限的操作将它们按照指定顺序转移到2号轨道。理解以下几点至关重要操作限制每次只能移动一节车厢1号轨道的车厢有两种选择直接通过1-3连接道进入3号轨道操作记为1-3通过两条连接道直接进入2号轨道操作记为1-23号轨道的车厢只能通过2-3连接道进入2号轨道操作记为3-2一旦车厢进入2号轨道就不能再移动输入输出规则输入是两行大写字母字符串第一行表示1号轨道的初始顺序从左到右第二行表示2号轨道的目标顺序需要输出最短操作序列或无法完成时的特定提示关键理解点第二行的目标顺序是指车厢进入2号轨道的顺序。例如样例1中的CBA表示C最先进入位于最右侧A最后进入位于最左侧。2. 问题抽象与算法选择将实际问题转化为数据结构问题是解题的关键一步。通过分析我们可以得出以下对应关系1号轨道输入序列只能从头部取出元素先进后出3号轨道典型的栈结构后进先出2号轨道目标序列一旦进入就不能再移动因此这个问题本质上是要验证能否通过栈的缓冲作用将输入序列转换为指定的输出序列——这正是栈的经典应用场景之一。2.1 算法思路详解我们采用以下步骤来解决这个问题初始化一个空栈和两个指针分别指向输入序列和目标序列的当前位置遍历输入序列中的每个车厢如果当前车厢与目标序列的当前需求匹配直接执行1-2操作否则将车厢压入栈中执行1-3操作检查栈顶车厢是否与后续目标需求匹配如果匹配弹出栈顶执行3-2操作否则继续处理输入序列最终检查如果所有车厢都能按需求进入2号轨道输出操作序列否则输出无法完成的提示关键点在每次直接匹配后都要检查栈中是否还有可以满足当前需求的车厢这就是样例ABCDE DCBAE的考察重点。3. 代码实现与逐行解析下面给出完整的C语言实现我们将分段解析关键代码逻辑。3.1 基础定义与输入处理#include stdio.h #include string.h #define MAX_LEN 80 int main() { char stack[MAX_LEN]; // 3号轨道栈 int top -1; // 栈顶指针 char train[MAX_LEN]; // 1号轨道初始序列 char fin[MAX_LEN]; // 2号轨道目标序列 int ops[MAX_LEN * 2]; // 存储操作序列(1:1-2, 2:1-3, 3:3-2) int op_count 0; // 操作计数 int possible 1; // 是否可能完成 // 读取输入 scanf(%s, train); scanf(%s, fin); int len strlen(train);这部分代码定义了必要的变量stack数组模拟3号轨道top是栈顶指针train和fin分别存储初始序列和目标序列ops数组记录操作序列op_count记录操作数量possible标志位表示是否可能完成调度3.2 核心调度逻辑int i 0, j 0; // i遍历trainj遍历fin while (i len possible) { if (train[i] fin[j]) { // 直接匹配执行1-2操作 ops[op_count] 1; i; j; // 检查栈中是否还有可匹配的车厢 while (top 0 stack[top] fin[j]) { ops[op_count] 3; top--; j; } } else { // 不匹配压入栈中(1-3操作) if (top MAX_LEN - 1) { stack[top] train[i]; ops[op_count] 2; i; } else { possible 0; // 栈溢出不可能完成 } } }这段代码实现了核心调度算法比较当前车厢(train[i])与目标需求(fin[j])如果匹配执行1-2操作并检查栈中是否有连续匹配如果不匹配执行1-3操作将车厢压入栈中3.3 最终检查与输出// 处理栈中剩余车厢 while (j len top 0 possible) { if (stack[top] fin[j]) { ops[op_count] 3; top--; j; } else { possible 0; } } // 输出结果 if (!possible || j ! len) { printf(Are you kidding me?); } else { for (int k 0; k op_count; k) { switch (ops[k]) { case 1: printf(1-2\n); break; case 2: printf(1-3\n); break; case 3: printf(3-2\n); break; } } } return 0; }最后这部分代码检查栈中是否还有可以满足目标需求的车厢根据是否完成所有需求输出相应结果如果成功按照记录的操作序列输出每一步操作4. 关键测试用例与避坑指南在实际解题过程中有几个容易出错的地方需要特别注意4.1 易错点分析目标序列的理解错误理解认为第二行输入表示2号轨道的最终物理顺序正确理解第二行输入表示车厢进入2号轨道的顺序即出站顺序栈的连续弹出在直接匹配后1-2操作必须检查栈顶是否还能继续满足后续需求这就是样例ABCDE DCBAE的考察重点在D匹配后栈中有CBA可以连续弹出操作序列的最短性题目要求输出最短操作序列这意味着只要1-2操作可行就不应该选择1-33-2的组合4.2 重要测试用例除了题目提供的样例以下测试用例能帮助你验证代码的健壮性测试用例预期输出检查重点ABC ACB1-21-33-2基本功能ABCD DCBA1-31-31-31-23-23-23-2完全逆序ABCDE DECBAAre you kidding me?不可能情况ABCDEF FEDCBA1-31-31-31-31-31-23-23-23-23-23-2长序列处理5. 算法优化与扩展思考虽然上述解决方案已经能够正确解决问题但仍有优化空间空间优化当前使用了三个数组(stack, train, fin)实际上可以只保留stack输入序列可以边读取边处理不需要完整存储时间效率当前算法时间复杂度为O(n)已经是最优可以通过减少循环内的条件判断来优化常数因子扩展问题如果允许更多的轨道问题会如何变化如果车厢可以重复算法需要如何调整// 优化后的输入处理示例 int ch; int i 0; while ((ch getchar()) ! \n ch ! EOF) { train[i] ch; } train[i] \0;通过本文的详细讲解你应该已经掌握了列车厢调度问题的核心解法。记住理解题目是第一步将实际问题抽象为数据结构问题是关键而代码实现只是这个过程的自然结果。现在你可以自信地去PTA上提交这个问题的解决方案了