BUAA(2021春)火车货运调度模拟(期末考试模拟题)——抽象过程,找到规律(找规律+栈双题解)

48 篇文章 154 订阅

看前须知

要点介绍和简要声明.

考试回顾

格式控制输入输出——期中考试模拟题(简单的分类讨论).

标识符的识别(期中考试题)——题目说的不清不楚但其实不难.

空闲空间申请模拟(期中考试题)——注意读题,难度其实一般.

网络打印机选择(北京学院数据结构18级期末压轴题)——伪树状数组(这题杀我)(ಥ_ಥ).

查家谱(士谔书院16级期末)——找最近公共祖先.

图的直径(士谔书院15级期末)——求有向图的最大路径:Floyd的妙用.

快逃离卷怪统治的BUAA——求树中的最短路径+中序和后序恢复二叉树.

网络打印机延迟率计算(士谔18级期末改编)——伪树状数组+树的直径.

题目内容

问题描述

某火车货场由A、B、C三段组成,见下图。现有一列货车停在A段上,由多个货物车厢组成,每节车厢信息包括编号和货物发往的目的地(车厢编号是唯一的,各节车厢发往的目的地可以相同,也可以不同)。当前停在A段的货车车厢发往目的地编组是乱的,需要按目的地由远至近进行重新编组,即车厢离车头越近其目的地越远,这样方便到达目的地时卸货(卸下相关车厢)。编组过程中车厢只能依次从A中移动至B或C中,或从B或C中移至A中,从B中不能直接移至C中,从C中也不能直接移至B中。编写一程序模拟货运车厢编组。
在这里插入图片描述
假设A、B、C三段交叉路口为各段的顶端,编组规则如下:

  1. 初始时所有车厢位于A中且均未完成编组,车头距离顶端最远;首先将其所有车厢从顶开始依次推进至B中。
  2. 从B中找到发往地最远的车厢中离顶最近的车厢,假设其为M。将M至顶的所车厢依次推进至A中,此时M一定位于A中顶端。
  3. 若A中M之后(即:M与车头之间)不存在未完成编组的车厢,则车厢M的编组完成了;若A中M之后存在未完成编组的车厢,则将M推进至 C中,将A中M之后所有未完成编组的车厢依次推进至B中,再将M由C中推进至A中,这样就完成了车厢M的编组;
  4. 重复步骤2-3,直到B中无车厢。

输入形式

先从控制台输入目的地个数(大于等于1,小于等于50),然后分行输入一组由地名(用不含空白符的英文字符串表示,字符个数不超过20)和里程(用大于等于1小于等于10000的整数表示)构成的发往目的地(由近至远序)里程表,地名和里程之间以一个空格分隔;在里程表之后输入车厢个数(大于等于1,小于等于50),然后分行输入一组由车厢编号(由四位数字组成)和发往目的地(目的地肯定在上述里程表中出现)构成的车厢信息,车厢编号和目的地之间以一个空格分隔,并且是从车头处开始依次输入每节车厢信息。

输出形式

假设一节车厢从某段推出称为一次pop操作,推进某段称为一次push操作。程序运行输出第一行为从车头开始编好组的车厢编号,中间用一个空格隔开,**最后一个车厢编号后也有一个空格。**第二行输出编组过程中A段中push操作的次数。

样例

【样例输入】

10
shijiazhuang 280
xingtai 390
xinxiang 610
zhengzhou 689
wuchang 1221
chibi 1339
yueyang 1434
changsha 1559
shaoguan 2057
guangzhou 2273
12
0039 guangzhou
5217 xingtai
0262 yueyang
7205 wuchang
3211 guangzhou
4893 shijiazhuang
2283 shaoguan
0890 guangzhou
8729 wuchang
6839 shijiazhuang
2122 changsha
3280 wuchang
【样例输出】
0039 3211 0890 2283 2122 0262 7205 8729 3280 5217 4893 6839
45

样例说明

首先输入由10个地名及其里程组成的里程表,然后输入了12节需要编组的车厢的编号和发往的目的地,即初始时处于A中的车厢,其中0039编号的车厢离车头最近。按照上面编组规则,首先将所有车厢推进至B中,这时0039车厢位于B的顶端,3280车厢位于底端,B中所有车厢中最远目的地为guangzhou、且离顶最近的车厢为0039,将其推进至A中,这时A中只有其一节车厢,其后没有未编组的车厢,0039车厢编组完成(即完成第一节车厢编组),此时A的push操作次数为1;接下来,B中剩余的11个车厢中,最远目的地依然为guangzhou,其离顶最近的车厢为3211,将该车厢及其上的3节车厢依次推进到A中,此时,3211车厢与火车头之间有三节车厢未完成编组,于是按规则将3211推进至C中,将7205、0262和5217依次推进至B中,再将C中的3211推进至A中,这时3211车厢编组完成(即完成第二节车厢编组),此时A的push操作次数为6;再依次按照上述步骤对B中剩余的车厢完成编组,直到B中无车厢。最后从车头开始将所有完成编组的车厢编号依次输出,由此得到一组发往目的地距离(从车头开始)由远至近的车厢序列。完成编组过程中A的push操作次数共有45次。

题解

思考和详解

其实看起来难,好像和栈有关,其实关系不大。这道题的第一个步骤就是把站点名转化为距离,方便后续操作。第二个步骤其实很简单,抽象一下,其实就是先在B里面找到第一个最大的然后把包括自己与之前的放入A,然后再把自己放入C,前面的回到B,自己在放回A.忽略中间过程,其实就是把第一个最大的放入到了A,然后B中的车厢数量-1,同时那个车厢之后的车厢集体前移,循环退出就标志就是B中没有车厢。置于数量,如果是第一个最大的在B顶,那么cnt+=1(因为该车厢不用进入C),如果是在中间的,那么就找到它,设它的下标为maxName,那么cnt=cnt+maxName+2(它前面有maxName个,所以推maxName次,然后自己进A 1次,到C后回A又1此,所以+2),这个题就迎刃而解了。

参考代码

本人写的找规律代码

#include<stdio.h>
#include<string.h>
#include<math.h>
#include<stdlib.h>
#include<ctype.h>
#include<stdbool.h>
#define M 1000000
struct station{
	char name[200];
	int dis;
}a[200];
struct stack{
	char NUM[200]; 
	int dis;
}s[200];
char findName[200];
int top=0,i,j,max,maxName,cnt=0;
int main()
{	
	int n,m;
	scanf("%d",&n);
	for(i=0;i<n;i++)
	{
		scanf("%s %d",a[i].name,&a[i].dis);
	}
	scanf("%d",&m);
	for(i=0;i<m;i++)
	{
		scanf("%s %s",s[i].NUM,findName);
		for(j=0;j<n;j++)
		{
			if(strcmp(findName,a[j].name)==0)
			s[i].dis=a[j].dis;
		}
	}
	while(m>0)
	{
		max=s[0].dis;
		maxName=0;
		for(i=0;i<m;i++)
		{
			if(s[i].dis>max)
			{
				max=s[i].dis;
				maxName=i;
			}
		}
		if(maxName==0)
			cnt++;
		else
			cnt=cnt+maxName+2;
		printf("%s ",s[maxName].NUM);
		for(i=maxName;i<m-1;i++)
		{
			s[i].dis=s[i+1].dis;
			strcpy(s[i].NUM,s[i+1].NUM);
		}
		m--;
	}
	printf("\n%d\n",cnt);	
	return 0;
}

完全按照题意利用栈的知识写的代码(由2073 子懿姐姐提供,有明确注释

#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include <stdlib.h>


struct mdd{        //目的地(mdd)结构体数组
char arr[25];      //目的地(arrival)名称
int n;             //距离
};
struct mdd q[55];

struct xhc{         //小火车(xhc)的车厢结构体数组
int num;           //车厢编号(不是四位数没有关系)
char arr[25];       //车厢目的地(arrival)
int test;          //是否完成编组
};
struct xhc l[55];

struct xhc stacka[100];//栈abc
struct xhc stackb[100];
struct xhc stackc[100];
int topa=-1,topb=-1,topc=-1;//栈顶abc
int count=0;//push计数

int main(){
int m,n,i,j;
int t=0,maxd=0,maxi,exi=-1;//maxd为每次循环时车厢目的地的最远距离,maxi为最远距离在目的地(mdd)数组内的编号
                          //exi(exist)为已经在a中编组好的车厢数量,t只是个工具人,不用管它。
int again=0;//A到M是否完全编组
scanf("%d",&n);
for(i=0;i<n;i++){
    scanf("%s %d",q[i].arr,&q[i].n);//读入目的地和距离
}
scanf("%d",&m);
for(i=0;i<m;i++){
    scanf("%d %s",&l[i].num,l[i].arr);//读入车厢编号和目的地
    stacka[++topa]=l[i];

}
maxd=0;
while(topa>=0){
    stackb[++topb]=stacka[topa--];//将A中所有东西压进栈B

}
while(topa<m-1){//只要A中车厢没有满就一直循环
        maxd=0;//重置B中车厢内最远距离
for(j=topb;j>=0;j--){
    for(i=n-1;i>=0;i--){
        if(strcmp(stackb[j].arr,q[i].arr)==0){
            t=q[i].n;
            if(maxd<t){maxd=t;maxi=i;}//查找出B中剩余车厢内最远距离车厢该距离在目的地(mdd)数组的编号
        }
    }
}
for(i=0;i<m;i++){
    stacka[++topa]=stackb[topb--];//把B中车厢一个个压进A
    count++;
    if(strcmp(stacka[topa].arr,q[maxi].arr)==0){
        break;//如果压入了最大距离的车厢就停止,此时这个车厢即为题目中的M
    }
}
stacka[topa].test=1;//A的栈顶M号车厢的test设置为1,即该车厢已经编组。
again=0;//again重置
for(i=exi+1;i<=topa;i++){
    if(stacka[i].test==0){again=1;break;}//上一次循环结束后A栈顶车厢为exi,在M到exi的车厢是否完全编组。
                                        //如果不是,again=1,如果是,again保持0.
}
if(again==0){exi=topa;}//如果again=0那么exi变到topa成为当前栈顶
else if(again==1){      //如果again=1
    stackc[++topc]=stacka[topa--];//把M车厢压到栈C
    while(topa>exi){
         stackb[++topb]=stacka[topa--];//将其他参与这次循环的所有车厢压回栈B
    }

stacka[++topa]=stackc[topc--];   //将M车厢压回栈A
count++;
exi+=1;                         //A中已编组车厢数+1
}
}
for(i=0;i<=topa;i++){
    if(stacka[i].num<1000&&stacka[i].num>=100){//如果车厢号码三位数,补输出一位0
        printf("0");
    }
   else if(stacka[i].num<100&&stacka[i].num>=10){//如果车厢号码两位数,补输出两位0
        printf("00");
    }
   else if(stacka[i].num<10&&stacka[i].num>=0){//如果车厢号码一位数,补输出三位0
        printf("000");
    }
   printf("%d ",stacka[i].num);//输出存储的int型车厢号码
}
printf("\n%d",count);//输出push次数count
return 0;
}

  • 5
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值