2023年8月1日中午,正在排队等着购买午饭,手机弹出一条推送“CSDN比赛… …”,心中一亮,参加!
2023年8月2日18:50,带着空空的肚子,坐在电脑前,等待开赛。(为什么句句不离吃つ﹏⊂,我真的不是一个吃货啊)
19点准时开赛,1个判断、2个选择、1个填空,共40分,都是关于编码理论知识的,而有猫彬同学毫无基础,蒙蒙猜猜,终得20。赛后就有点想读下比赛推荐书目了,补充下自己的知识盲区。
接着到了最喜欢的2个编程题目,共60分。咱们展开说说。
(更多博文,欢迎来我的博客学习交流have_a_cat_PHP,C/C++,大厂热门笔试面试-CSDN博客)
一、小球游戏
【题目描述】
某台有10个小球的游戏机,其设定的规则如下:
每一轮游戏在开始之前会把编号为0到9的小球依次放入从左到右编号也为0到9的10个位置;游戏开始后会快速对调任意两个球的位置若干次,并在结束时要求观众写出从左到右的小球编号顺序,写对就得奖。
由于速度很快,所以直接靠观看写对很难。但有个程序员发现这台游戏机其实有一个固定的长度为n的操作序列数据库,每一轮游戏都是随机取一个起始操作序列编号和一个结束操作序列编号(操作序列编号从1到n)并从起始到结束依次执行每个操作序列编号对应的操作,而每个操作序列编号对应的操作就是对该次操作指定的两个编号的位置上的小球进行对调。
现在给出操作序列数据库和每一轮游戏的起始操作序列编号和结束操作序列编号,求每轮游戏结束时从左到右的小球编号顺序。
【解题思路】
这个题目有点长,需要耐心读一下,边读边快速提取关键信息,来我们一起读一遍。
①“把编号为0到9的小球依次放入从左到右编号也为0到9的10个位置”-->设置数组h[10],且令h[i]=i,就是h[位置]=小球编号
②“结束时要求观众写出从左到右的小球编号顺序”-->输出h[0]至h[9]
③“固定的长度为n的操作序列数据库”-->设置数组f[n][2],f[][0]和f[][1]记录对调的位置
(更多博文,欢迎来我的博客学习交流have_a_cat_PHP,C/C++,大厂热门笔试面试-CSDN博客)
④“现在给出操作序列数据库和每一轮游戏的起始操作序列编号和结束操作序列编号,求每轮游戏结束时从左到右的小球编号顺序”-->这是一道考察模拟的题。
模拟题就话不多说,模拟题目描述的过程后,必然AC
【代码语言】
将题目转换为程序员更容易懂的语言就是
已知且每轮游戏不变,现有数组f[n][2],其中数据来源于读入;
已知,现在有数组h[10],初始化为h[i]=i;给出对f的起始操作序列编号c和结束操作序列编号d。
模拟,按照f[c][]到f[d][]中存储的序列,交换h中的数据,即
for(j=c;j<=d;j++)
{
交换h[f[j][0]]和h[f[j][1]]中的数字
}
输出,输出h[0]至h[9]
循环,将上述游戏进行m轮
【C代码】
#include <stdio.h>
int main()
{
int i,j,k;/*用于for循环的变量*/
int n;/*操作序列的长度*/ scanf("%d",&n);
int m;/*游戏轮数*/ scanf("%d", &m);
int f[102400][2];/*操作序列,这里盲猜出题方最大给102400个序列*/
for(i=1; i<=n; i++)/*读入序列f*/
{
scanf("%d",&j); f[i][0]=j;
scanf("%d",&j); f[i][1]=j;
}
int c;/*起始操作序列编号*/
int d;/*结束操作序列编号*/
int h[10];//位置
for(i=0;i<m;i++)/* 进行m轮游戏 */
{
for(j=0; j<=9; j++)
{
h[j] = j;/*初始化,把编号为0到9的小球依次放入从左到右编号也为0到9的10个位置*/
}
scanf("%d",&c);/*读入本轮游戏起始操作序列编号*/
scanf("%d",&d);/*读入本轮游戏结束操作序列编号*/
for(j=c;j<=d;j++)/*交换h[f[][0]]和h[f[][1]]中的数字*/
{
k=h[f[j][0]];
h[f[j][0]]=h[f[j][1]];
h[f[j][1]]=k;
}
for(j=0; j<=9; j++)/*输出*/
{
if(j>0)printf(" ");
printf("%d",h[j]);
}printf("\n");
}
}
【有话想对主办方说】
建议以后的判题程序,可以自动过滤答案末尾的多余空格。
在此题中,我的①输出答案格式,在[自测调试]中可以通过,在[提交]中0%;我的②输出答案格式,在[自测调试]中可以通过,在[提交]中100%;
①输出答案格式 | ②输出答案格式 |
for(j=0; j<=9; j++)/*输出*/ { printf("%d ",h[j]); }printf("\n"); | for(j=0; j<=9; j++)/*输出*/ { if(j>0)printf(" "); printf("%d",h[j]); }printf("\n"); |
(更多博文,欢迎来我的博客学习交流have_a_cat_PHP,C/C++,大厂热门笔试面试-CSDN博客)
二、王子闯闸门
【题目描述】
波斯王子要去救被贾法尔囚禁的公主,但贾法尔用黑魔法在他面前设置了编号从1到n的n道闸门。从王子的位置到1号闸门需要1秒,从n号闸门到公主所在的位置也需要1秒,从p号闸门到p+1或p-1号闸门都需要1秒。每过1秒钟,王子都必须决定选择前进一道闸门、后退一道闸门或停在原地这三种动作中的一种。当然,王子不能选择移动到关闭状态的闸门而只能选择开启状态的闸门。在王子做出动作选择后,闸门也可能会有关闭和开启的动作,如果王子做完动作后,其所在的闸门在该秒内的动作是从开启变为关闭则他就会被闸门夹死。
现在给出闸门数量n和m个闸门的动作时刻表,求波斯王子需要多少秒才能救出公主。
【解题思路】
这个题目像一个童话故事,哥们,别陷在爱情里,来我们一起边读边快速提取关键信息。
①“从p号闸门到p+1或p-1号闸门都需要1秒”-->相邻闸门间耗时1秒
②“王子都必须决定选择前进一道闸门、后退一道闸门或停在原地这三种动作中的一种”-->策略操作可-1后退/0原地/+1前进
③“王子不能选择移动到关闭状态的闸门而只能选择开启状态的闸门”、“闸门也可能会有关闭和开启的动作”、“m个闸门的动作时刻表”-->闸门有些时间不能通过
(更多博文,欢迎来我的博客学习交流have_a_cat_PHP,C/C++,大厂热门笔试面试-CSDN博客)
④“从王子的位置到1号闸门需要1秒,从n号闸门到公主所在的位置也需要1秒”-->王子位置为0,公主位置为n+1(这里反复读了3遍后,神来之思,从此,这个故事就可以没有公主和王子了,哈哈哈,简化)
看似繁杂,其实,这就是一道 最长不下降子序列 的变型$_$
【代码语言】
已知,有数组f[n],0表示王子的站位,1表示1号闸门的位置,2表示2号闸门的位置… …n表示n号闸门的位置,n+1表示公主的站位。f[i]表示到达这个位置时最少花费的时间,初始化f[0]=0;
已知,有数组y[m][3],用于存放闸门的动作时刻表,第y[][0]号闸门在时间y[][1]到y[][27之间关闭;
已知,有数组x[n][2],用于标明闸门有没有、有几段时间会关闭,x[i][0]和x[i][1]初始化为-1,x[i][0]大于等于0表示闸门i有时间会关闭,具体闸门的动作时刻表为y[x[i][0]][]至y[x[i][1]][](这里尤其要注意,闸门会有超过1段时间关闭,比如闸门2会在第2-3秒关闭,且会在第5-6秒关闭,且会在9-10秒关闭,当时解题时,第一次提交没考虑到,如果按一个闸门只有1段时间关闭的话,就是通过率50%)
最长不下降子序列 的变型,用p表示当前的位置,p初始化为0;
whiel(p < (n+1))/*在没有到达n+1位置时,执行*/
{
if(f[p] <= f[p-1]) f[p] =f[p-1]+1;/*到达当前位置p用时=到达位置p-1用时+1秒*/
if(在这1秒到达p位置,正好赶上p位置关闭)
{
f[p]=最近p位置关闭结束的时间+1秒;
(更多博文,欢迎来我的博客学习交流have_a_cat_PHP,C/C++,大厂热门笔试面试-CSDN博客)
f[p-1]= 最近p位置关闭结束的时间;
p--;//相当于在这段时间里,执行了后退+原地策略
}
}
输出,f[p+1]
【C代码】(考试当场写的代码自觉不够优美,思考一夜后,写了个新版,展示如下)
#include<stdio.h>
int main()
{
int i,j,k;/*循环变量备用*/
int n;/*闸门数量*/ scanf("%d",&n);
#define N_MAX 102400;/*盲猜n最大为102400,即最多有102400-2个闸门*/
int f[N_MAX];/*到达i时花费的最少时间f[i]*/
for(i = 0; i < N_MAX; i++)/*初始化*/
{
f[i]=0;
x[0]=-1;
x[1]=-1
}
int m;/*闸门关闭的时间组数*/ scanf("%d",&m);
#define M_MAX 102400;/*盲猜m最大为102400,即最多有102400个闸门的动作时刻表*/
int y[M_MAX][3];/*闸门的动作时刻表,其中第i条,y[i][0]表示关闭时间,y[i][1]表示再次开启的时间*/
int a,b,c;/*用于读入数据的变量,第a号闸门在时间b到c之间关闭*/
for(i = 0; i < m; i++)
{
scanf("%d",&a); scanf("%d",&b); scanf("%d",&c);
y[i][0]=a; y[i][1]=b; y[i][2]=c;
}
/*对动作时刻表按照闸门号码排序-这里用插入排序*/
for(i = 0; i < m; i++)
{
for(j = 0; j < m; j++)
{
if(y[i][0] < y[j][0])
{
int u0,u1,u2;
u0=y[i][0]; u1=y[i][1]; u2=y[i][2];
for(k=i;k>j;k--)
{
y[k][0]=y[k-1][0]; y[k][1]=y[k-1][1]; y[k][2]=y[k-1][2];
}
y[j][0]=u0; y[j][1]=u1; y[j][2]=u2;
break;
}
}
}
int x[N_MAX][2];/*闸门i有好多条时刻表,从x[i][0]到x[i][1]都是闸门i的时刻表*/
for(i = 0; i < m; i++)
{
if(x[y[i][0]][0] < 0)
{
x[y[i][0]][0]=i;
}
x[y[i][0]][1]=i;
}
int p; p=1; /*当前位置,0为王子,n+1为公主*/
f[0]=0;
while(p <= (n+1))
{
if(f[p] <= f[p-1]) f[p]=f[p-1]+1;
if(x[p][0] >= 0)/*该闸门有关闭时刻表*/
{
int q=-1;/*如果当前闸门关闭,则置为1,否则p++*/
for(i=x[p][0];i<=x[p][1];i++)/*遍历该闸门的每条时刻表*/
{
if((y[i][1]<=f[p])&&(f[p]<=y[i][2]))/*当前时间闸门关闭*/
{
f[p] = y[i][2]+1;
f[p-1]=y[i][2];
p--;/*当前闸门关闭,后退!*/
q=-q;
break;
}
}
if(q<0) p++;/* 当前闸门开启,前进!*/
}
else
{
p++;/* 当前闸门开启,前进!*/
}
}
printf("%d",f[n+1]);
}
【有话想对主办方说】
虽然比赛结束了,但还是想测一测我这个新思考出来的代码版本,有没有可以测试的入口呢?或者是,以后会不会开放往期编程赛题的练习入口?
三、附上考试结束时获得的得分报告
每一次坐在电脑前,指尖飞快的流出代码,都是身心最愉悦的时间。 ----2023年8月2日