第二次双周赛

7-3 跳跃

分数 300

全屏浏览题目切换布局

作者 Drizzle

单位 山东科技大学

Drizzle 被困到一条充满数字的方块路中,假设这条路由一个非负的整数数组m组成,Drizzle 最开始的位置在下标 start 处,当他位于下标i位置时可以向前或者向后跳跃m[i]步数,已知元素值为0处的位置是出口,且只能通过出口出去,不可能数组越界,请你通过编程计算出Drizzle能否逃出这里。

要求:

输入:第一行输入数组m的长度n 第二行输入数组元素,空格分割开 第三行输入起始下标start

示例:

输入:

7
4 2 3 0 3 1 2
5

输出:

True

范围:

  • 1 <= m.length <= 5 * 10^4
  • 0 <= m[i] < m.length
  • 0 <= start < m.length

代码长度限制

16 KB

时间限制

400 ms

内存限制

64 MB

思路

这道题本身没啥思维难度,就是用搜索实现是否可以抵达某个位置,但是我在考试的时候被卡住了😅,因为自己误以为出口只有一个,所以无论怎么改都写不对,在这里记录一下。

虽然感觉他确实没说清楚,但是自己以后在这里还是要放一个心眼,要是出现错误之后,自己还能找到原因。(就怕出题人语文不咋地)

代码实现

#include<iostream>
#include<queue>
using namespace std;
int main()
{
    int n;
    int End=-1;
    int m[50004]={0};
    bool vis[50004]={0};
    cin>>n;
    for(int i=0;i<n;i++) 
    {
        cin>>m[i];
        if(m[i]==0) End=i;
    }
    int start;
    cin>>start;
    queue <int>q;
    q.push(start);
    
    while(!q.empty())
    {
        int temp=q.front();
        q.pop();
        int t1=temp+m[temp];
        int t2=temp-m[temp];
        if(t1>=0 && t1<n && m[t1]==0 || t2>=0 && t2<n && m[t2]==0) 
        {
            cout<<"True"<<endl;
            return 0;
        }
        
        if(t1>=0 && t1<n && vis[t1]==0) 
        {
            q.push(t1);
            vis[t1]=1;
        }
        if(t2>=0 && t2<n && vis[t2]==0)
        {
            q.push(t2);
            vis[t2]=1;
        }
    }
    cout<<"False"<<endl;
    return 0;
}

7-4 最长光路

分数 300

全屏浏览题目切换布局

作者 neuqAcmClub

单位 东北大学秦皇岛分校

小明在做丁达尔效应的实验。
他用胶体填满了一个有 N 行, M 列个格子的透明盒子。同时,为了使实验效果更好,他在盒子的某些格子中装入一些双面镜(用“/”和“\”表示)。
当光线找到双面镜的时候,光路会反射,方向会有90度的转换(如下图所示)。

reflection.JPG

小明在填充胶体的时候产生了失误,使得有些格子光线无法穿过(我们可以认为这些格子把光线都吸收了),这些格子用大写字母“C”来表示。

现在,小明已经制作好了实验装置,他将激光光源放在第s**x行的第sy个格子上。激光光源可以朝向上下左右四个方向,分别用URDL四个字母表示(“U”-上, “R”-右, “D”-下, “L”-左)。

为了让实验效果更好,小明希望光的光路越酷越好。
对小明来说,最酷的光路就是包含环的光路,这种情况下,光不会射向盒子外面,而是一直在盒子内循环(数据保证光源处的点是光线可以通过,即起点一点是".")。
如果光源朝各个方向摆设,最终光线都会射向盒子外部,那么小明认为经过格子最多的光路是最酷的。

现在小明想知道光源朝哪个方向放置光路最酷。
如果光路是不包含环的,那么他还想知道光路经过的格子数目是多少。

注意

如果有多个方向是最优解,那么我们按照“U”-上, “R”-右, “D”-下, “L”-左的优先级来选择最终的答案,即如果向左和向右都是最优解,我们选择向右的方案。

同时,温馨提醒,c++中“\”符号可以用'\\'来表示

输入格式:

第一行两个数 N,M(1≤N,M≤500) 表示有NM 列的格子。
接下来 N 行, 每行 M 个字符,表示盒子的具体情况。 “/”和“\”表示装有不同朝向的镜子的格子。“C”表示光线无法通过的格子。“.”表示正常且没有镜子的格子。
最后输入两个数字s**x,sy表示光源所在的点。

输出格式:

第一行输出一个大写字母,表示最酷的光路应该超哪个方向摆放。
第二行输出光路经过的格子数。如果光路中包含环,则换成输出字符串“COOL”。

输入样例1:

5 5
../.\
.....
.C...
...C.
\.../
3 3

输出样例1:

U
17

输入样例2:

5 7
/.....\
../..\.
\...../
/.....\
\.\.../
3 3

输出样例2

R
COOL

样例解释

S为起点
../.\
.....
.CS..
...C.
\.../

'U' 方向
*.***
*.*.*
*C*.*
*..C*
*****
17个格子


'R'方向
../.\
.....
.C***
...C.
\.../
3个格子


'D'方向
../.\
.....
.C*..
..*C.
\.*./
3个格子


'L'方向
../.\
.....
.C*..
...C.
\.../
1个格子

代码长度限制

16 KB

时间限制

400 ms

内存限制

64 MB

思路

刚开始没啥想法,就是一顿模拟+爆搜,感觉长宽都在500之内,还挺友好。然后就傻乎乎地写出来一个超级长的代码,交上去之后才发现有两个段错误,很明显的爆栈……

然后就想着是不是哪个地方死循环了……没有

又想着能不能在一些语法上进行优化……有没有

心态炸裂(╥﹏╥)

然后~~(为了偷懒,不想再写一遍其他的搜素写法)~~我就决定再看看有没有啥其他的大优化方式。我从效率差的地方开始看起,发现每次调用一下函数,只能走一个,然后我就想着,如果能实现一次直接走到尽头(边界,镜子或者无法穿过的格子,那速度(除了满屏幕的镜子)一定会快很多。于是就有了下面的代码。

顺带提一嘴,这道题目给的数据点没包括起始点是镜子的情况🤣,但确实是可能存在的。

代码实现

#include<bits/stdc++.h>
using namespace std;
const int maxn=505;
char Map[maxn][maxn];
int n,m;
int cnt=0;
int sx,sy;
int dir_x[4]={0,0,1,-1};
int dir_y[4]={1,-1,0,0};
char direction[4]={'U','R','D','L'};
bool flag=false;
int ret[4]={};
int temp,step;
int conv(char c)
{
    if(c=='U') return 3;
    if(c=='D') return 2;
    if(c=='L') return 1;
    if(c=='R') return 0;
    return 0;
}
void mani(char dir,int now_x,int now_y,char original)
{
    if(Map[now_x][now_y]=='C' || now_x>n || now_x<1 || now_y>m || now_y<1)
    {
        return ;
    }
    if(Map[now_x][now_y]=='/')
    {
        cnt++;
        if(dir=='U') mani('R',now_x,now_y+1,original);
        if(dir=='D') mani('L',now_x,now_y-1,original);
        if(dir=='L') mani('D',now_x+1,now_y,original);
        if(dir=='R') mani('U',now_x-1,now_y,original);
        return ;
    }
    if(Map[now_x][now_y]=='\\')
    {
        cnt++;
        if(dir=='U') mani('L',now_x,now_y-1,original);
        if(dir=='D') mani('R',now_x,now_y+1,original);
        if(dir=='L') mani('U',now_x-1,now_y,original);
        if(dir=='R') mani('D',now_x+1,now_y,original);
        return ;
    }
    if(dir==original && now_x==sx && now_y==sy && cnt>0)
    {
        flag=true;
        cout<<dir<<endl;
        cout<<"COOL"<<endl;
        exit(0) ;
    }

    temp=conv(dir);
    step=1;
    while(Map[now_x+dir_x[temp]*step][now_y+dir_y[temp]*step]=='.'&& now_x+dir_x[temp]*step!=sx && now_y+dir_y[temp]*step!=sy) step++;
    cnt+=step;
    mani(dir,dir_x[temp]*step+now_x,dir_y[temp]*step+now_y,original);
}
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            cin>>Map[i][j];
        }
    }
    
    cin>>sx>>sy;

    for(int i=1;i<=4;i++)
    {
        if(flag==true) break;
        cnt=0;
        mani(direction[i-1],sx,sy,direction[i-1]);
        if(flag==false)
        {
            ret[i-1]=cnt;
        }
    }
    int *p=max_element(ret,ret+4);
    int ret_num=p-ret;
    cout<<direction[ret_num]<<endl;
    cout<<ret[ret_num]<<endl;
    return 0;
}

7-5 回文数文回

分数 300

全屏浏览题目切换布局

作者 neuqAcmClub

单位 东北大学秦皇岛分校

我们称一个数是回文的,当且仅当它正着读和倒着读是相同的。

例如11或11455411是回文的,而10或1919810不是回文的。

现在给定一个数n,你需要求出区间[108,n]中所有的回文数。

输入格式:

一行一个整数n(108≤n<109)

输出格式:

输出一行一个数,表示题目所求区间中回文数的数量。

输入样例:

在这里给出一组输入。例如:

100000001

输出样例:

在这里给出相应的输出。例如:

1

代码长度限制

16 KB

时间限制

500 ms

内存限制

64 MB

思路

暴力枚举,游戏结束

暴力打表,游戏结束

好的,开始正解:

我们仔细看,会发现题目要计算的回文数都是9位数。打表是O(1)做法,是可以实现的,但是暴力枚举判断是不可行的,考虑到数据太大,根本做不到时间在0.5秒内完成计算。

这就需要我们另辟蹊径了。

对于一个回文数,他一定是回文的(说了个寂寞),而且是对称的。我们可以通过写出任意一个五位数来构造出9位数字的回文数,而且这种构造是完全的(即,能把所有情况的包括在内)。因此我们要从构造的角度去计算回文数的个数(而不是循环枚举的角度)。

为了方便,我们构造的时候用左边的数构造出右边的数。

「解释一下为什么用左边的数,而不用右边的数:

观察之后易于得知,当右边构造左边时,计算不能从00001开始计算,而是要倒着算,因为正着算得出来的数会超过给定的n,而且上限也还要确定,这样一来就比较麻烦;而左边构造右边,除了某种特殊情况,10000~左边的数,都是可以构造出右边的。下面会去考察这种特殊情况。」

接下来就是考虑怎么通过构造来计算。

对于给定的n,左半部分的数,我们标记为L1,右半部分的数,我们标记为L2。把L1翻转过后,设为L1’。接下来有两种情况:

  1. L 1 ′ ≤ L 2 L1' \leq L2 L1L2 那么对于10000~L1之间的任何一个数都可以成为构造的数,此时的种类数为L1-10000+1
  2. L 1 ′ > L 2 L1' \gt L2 L1>L2 那么对于10000~L1’-1之间的任何一个数都是可以构造的,但是L1’是不可以的,因为右半部分的数没办法达到L1’(否则构造出来的数就超过了n)此时种类数为L1-10000

这样一分类之后,我们就可以得到我们的答案了。

代码实现

#include<iostream>
#include<string>
#include<algorithm>
using namespace std;
int main()
{
    string n;
    cin>>n;
    string l_s=n.substr(0,5);
    string r_s=n.substr(4,5);
    string l_s_re=l_s;
    reverse(l_s_re.begin(),l_s_re.end());
    int value=stoi(l_s);
    if(l_s_re>r_s)value--;
    cout<<value-1e4+1<<endl;
    return 0;
}

小结

这次双周赛考的不理想,我归结为以下几个原因:

  1. 上周班级工作事情太多了,在代码方面给的时间太少了,导致打代码不是很顺畅
  2. 周末的时候开了学代会,连续的5个小时的会让自己生理上和心理上都很疲惫
  3. 在写第三题的时候,自以为是地加条件,也就是考虑不周全,导致在这道题目debug太久, 而且还没写出来,同时又影响了考试的心态,导致后面的题目都不想写。
  4. 考试心态没把握好,对一道题目的时间把控还要提升。不要被一道题卡死!不要被一道题卡死!!不要被一道题卡死!!!

这次考的不好有客观的,也有主观的,希望下可以继续克服它们!

在希望与失望的决斗中,如果你用勇气与坚决的双手紧握着,胜利必属于希望 ——普利尼

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值