模拟算法刷题笔记【蓝桥杯】

发布于:2023-01-04 ⋅ 阅读:(242) ⋅ 点赞:(0)

模拟

定义:将题目中的意思用代码 模拟 出来

例题

安卓图案解锁

栗主席(lizi)是某xxxx大学的一个不得了的程序猿,然而没想到吧,他竟然有女盆友,我们假设为QAQ!!!
那天,QAQ问栗子:你的小米5s的图像解锁密码到底是多少?
栗子:嘛?我仔细想想…
QAQ:你仿佛在逗我…
栗子:我的图像解锁用过好多次密码,后来都是用指纹解锁,所以忘记密码辣。但是我记得可能是那几个密码
QAQ:那你务必告诉我…
栗子: …

然后,栗子就写下了一堆可能的密码,安卓图案解锁中,数字对应的位置已经标出。
但是栗子当然不想把真正的密码告诉QAQ,所以给QAQ的一系列的密码中,甚至有一些密码,是不符合安卓图案解锁的规则的。
QAQ也知道栗子肯定不老实,给了很多错的密码,甚至不符合规则的密码,所以想请你来找出,哪些密码是不符合规则的。
题目
安卓图案解锁的密码有这样的一些特点:
1.每个数字最多只会被使用一次。
2.如果想直接连接两个数字,但是线段中会经过另一个数字,当且仅有那个数字已经在之前就被使用过了,才会合法。(比如你想从1直接连接到9,那么要么是1->3->9,要么是3在之前已经被使用过了,然后才能直接从1->9)

输入描述

多组输入
每组输入占一行,包含一串数字(1~9),长度不超过30

输出描述

输出这个安卓图案解锁是否合法,如果合法输出"YES",反之输出"NO" (请参照样例输出,不要输出引号)

示例1

14569
1953
15963
15953

输出

YES
NO
YES
NO

  • 这个题目就是典型的模拟,因为我们不是真的用安卓手机去试验密码,而是用代码去模拟
  • 题目的主要思路是:
    1. 一个2维数组模拟图片中的9位数字键盘
    2. 找到键盘的数字关系

题解

#include <bits/stdc++.h>
using namespace std;
bool check(string &s){
    int x,y,ex_x,ex_y;
    int length = s.size();
    bool numeric[3][3];                //设一个2维数组模拟数字键盘
    memset(numeric, false, 9);
    ex_x = (s[0] - '1') % 3;           //ex_x表示之前的x,-'1' 是因为数组只能读0,1,2
    ex_y = (s[0] - '1') / 3;             //x用%,y用/    这里就是特殊数字关系
    numeric[ex_y][ex_x] = true;
    for(int i = 1; i < length ; ++i){
        x = (s[i] - '1') % 3;
        y = (s[i] - '1') / 3;
        if(numeric[y][x]) return false;
        if(( ex_x + x ) % 2 == 0 && (( ex_y + y ) %2 == 0) && !numeric[(y + ex_y) / 2][(x + ex_x) / 2]) return false;              //这里也是特殊的数字关系,前面两个判断是看是否跳过数字周围的数字(比如:1->9),后面的判断是看中间的被跳过的数字是否被使用
        numeric[y][x] = true;
        ex_x = x;
        ex_y = y;
    }
    return true;
}

int main(){
    string t;
    while(cin>>t){
        if(check(t)) printf("YES\n");
        else printf("NO\n");
    }
}

Captcha Cracker

www.02469.com(本网页纯属虚构,如有雷同,纯属巧合),是一个资源丰富的教学资源网站,好学的SK同学经常访问这个网站。通常来说,网站为了安全考虑,登录的时候都需要用户输入验证码,这就让SK同学非常不爽了。

SK同学希望你能帮他写一个程序来自动识别验证码的内容,验证码由小写字母和阿拉伯数字组成,你需要识别出其中所有的0,2,4,6,9以及这5个数字对应的英文单词,并按照它们在验证码中出现的顺序以数字形式输出。

为了表示感谢,SK同学愿意跟你分享他私藏的教学资源。

在这里插入图片描述

输入描述

第一行是一个正整数T(≤ 10),表示测试数据的组数, 每组测试数据只有一行,包含一个长度不超过100000的只由小写字母和阿拉伯数字组成的非空字符串。

输出描述

对于每组测试数据,输出一行字符串,表示识别出的验证码。

示例1
输入

2
onetwothreefourfiveseven
0two4six6siiiiix

输出

24
02466

说明

0 - zero
2 - two
4 - four
6 - six
9 - nine

  • 这道题不算难,但是我当时用了很复杂的方法,其实很简单就能完成

题解

#include <bits/stdc++.h>

using namespace std;
char c[100001];
int main()
{
    int n;
    scanf("%d",&n);
    while(n--)
    {
        cin>>c;
        int l=strlen(c);
        for(int i=0;i<l;i++)
        {
            if(c[i]=='0'||c[i]=='2'||c[i]=='4'||c[i]=='6'||c[i]=='9')
                cout<<c[i];
            if(c[i]=='z'&&c[i+1]=='e'&&c[i+2]=='r'&&c[i+3]=='o')
                cout<<0;
            if(c[i]=='t'&&c[i+1]=='w'&&c[i+2]=='o')
                cout<<2;
            if(c[i]=='f'&&c[i+1]=='o'&&c[i+2]=='u'&&c[i+3]=='r')
                cout<<4;
            if(c[i]=='s'&&c[i+1]=='i'&&c[i+2]=='x')
                cout<<6;
            if(c[i]=='n'&&c[i+1]=='i'&&c[i+2]=='n'&&c[i+3]=='e')
                cout<<9;
        }
        cout<<endl;
    }
    return 0;
}

[NOIP1999]回文数

若一个数(首位不为零)从左向右读与从右向左读都一样,我们就将其称之为回文数。

例如:给定一个10进制数56,将56加65(即把56从右向左读),得到121是一个回文数。
又如:对于10进制数87:
STEP1:87+78 = 165 STEP2:165+561 = 726

STEP3:726+627 = 1353 STEP4:1353+3531 = 4884

在这里的一步是指进行了一次N进制的加法,上例最少用了4步得到回文数4884。

写一个程序,给定一个N(2<=N<=10或N=16)进制数M(100位之内),求最少经过几步可以得到回文数。如果在30步以内(包含30步)不可能得到回文数,则输出“Impossible!”
进制N>10时,使用大写’A’字母表示10,'B’表示11,…,'E’表示16

输入描述

两行,分别为N,M

输出描述

STEP=ans

示例一
输出

9
87

输入

STEP=6

  • 这道题首先注意到STEP是一个数字加上它的回文,然后判断得出的数是不是回文,如果是就输出STEP的值
  • 其次,注意到N是进制,然后设置进制进位的算法(注意十进制以上的要转换大写字母)

题解

#include <bits/stdc++.h>
using namespace std;
bool check(int a[],int length){
    for(int i=0;i<length/2;++i){
        if(a[i]!=a[length-1-i]) return 0;
    }
    return 1;
}
 
int main(){
    int N;
    string M;
    cin>>N>>M;
    int a[100005],b[100005],step=0;
    for(int i=0;i<M.size();++i){
        if('A'<=M[i]&&M[i]<='F') a[i]=M[i]-'A'+10;                 //高于十进制的进行转换
        else a[i]=M[i]-'0';
    }
    int length=M.size();
    while(step<=30&&!check(a,length)){
        for(int i=0;i<length;++i) b[i]=a[length-1-i];
        for(int i=0;i<length;++i) {
            a[i]=a[i]+b[i];
            if(a[i]>N-1){                                                           //大于进制位,就进位
                ++a[i+1];
                a[i]%=N;
                if(i==length-1) ++length;
            }
        }
        ++step;
    }
    if(step>30) printf("Impossible!\n");
    else printf("STEP=%d",step);

[NOIP2016]玩具谜题

小南有一套可爱的玩具小人,它们各有不同的职业。
有一天,这些玩具小人把小南的眼镜藏了起来。小南发现玩具小人们围成了一个圈,它们有的面朝圈内,有的面朝圈外,如下图:
在这里插入图片描述
这时 singer 告诉小南一个谜题:「眼镜藏在我左数第 3 个玩具小人的右数第 1 个玩具小人的左数第 2 个玩具小人那里。」

小南发现,这个谜题中玩具小人的朝向非常关键, 因为朝内和朝外的玩具小人的左右方向是相反的:面朝圈内的玩具小人,它的左边是顺时针方向,右边是逆时针方向;而面向圈外的玩具小人,它的左边是逆时针方向,右边是顺时针方向。
小南一边艰难地辨认着玩具小人,一边数着:
singer 朝内,左数第 3 个是 archer
archer 朝外,右数第 1 个是 thinker
thinker 朝外,左数第 2 个是 writer
所以眼镜藏在 writer 这里!
虽然成功找回了眼镜,但小南并没有放心。如果下次有更多的玩具小人藏他的眼镜,或是谜题的长度更长,他可能就无法找到眼镜了。所以小南希望你写程序帮他解决类似的谜题。这样的谜题具体可以描述为:
有 n 个玩具小人围成一圈,已知它们的职业和朝向。现在第 1 个玩具小人告诉小南一个包含 m 条指令的谜题。其中第 i 条指令形如「左数/右数第 si 个玩具小人」。你需要输出依次数完这些指令后,到达的玩具小人的职业。

输入描述

输入的第一行包含两个正整数 n, m,表示玩具小人的个数和指令的条数。
接下来 n 行,每行包含一个整数和一个字符串,以逆时针为顺序给出每个玩具小人的朝向和职业。其中 0 表示朝向圈内,1 表示朝向圈外。保证不会出现其他的数。字符串长度不超过 10 且仅由小写字母构成,字符串不为空,并且字符串两两不同。整数和字符串之问用一个空格隔开。
接下来 m 行,其中第 i 行包含两个整数 ai, si,表示第 i 条指令。若 ai = 0,表示向左数 si 个人;若 ai = 1,表示向右数 si 个人。保证 ai 不会出现其他的数。1 ≤ si < n。

输出描述

输出一个字符串,表示从第一个读入的小人开始,依次数完 m 条指令后到达的小人的职业。

示例一
输入

7 3
0 singer
0 reader
0 mengbier
1 thinker
1 archer
0 writer
1 mogician
0 3
1 1
0 2

输出

writer

说明

这组数据就是「题目描述」中提到的例子。

示例二

10 10
1 C
0 r
0 P
1 d
1 e
1 m
1 t
1 y
1 u
0 V
1 7
1 1
1 4
0 5
0 3
0 1
1 6
1 2
0 8
0 4

输出

y

备注:

1 ≤ n, m ≤ 100000

  • 这道题为每个名字,对应好相应的正反,然后根据命令对应(模拟成一个圆圈)
  • 我的题解中记逆时针为正方向
#include <bits/stdc++.h>
using namespace std;
int main(){
    int n,m,a[100005],res=0;
    scanf("%d%d",&n,&m);
    string s[100005];
    for(int i=0;i<n;++i){
        cin>>a[i]>>s[i];
        if(!a[i]) a[i]=1;
        else a[i]=-1;                       //朝外记为-1
    }
   while(m--){
       int i1,i2;
       scanf("%d%d",&i1,&i2);
       if(i1==0)i1=-1;                     //此处,向左为反方向(契合前面的朝外为-1,朝外的左边为逆时针,朝内的右边为逆时针)
       res+=i1*a[res]*i2;
       res=(res+n)%n;                  //如果是负数的话,+n再%n也会回到它的相应位置,正数就是它本身,不会出现res=0的情况
   }
    cout<<s[res]<<endl;
}

总结

  1. 模拟类型的题目时,首先跟着题目意思,把题目读懂,按照题目要求即可,不需要预先建模。
  2. 如果可以用已知的代码知识一比一模拟出题目效果最佳(如前文中的安卓图案解锁 直接可以模拟出9位键盘),但如果没找到很契合的代码知识时,不要强求一比一模拟(如:[NOIP2016]玩具谜题 这种,仅凭我目前的代码知识来看,无法找到一个真正的圆形的容器,容纳玩具。但是用2个数组,一个存方向,一个存名字,再根据数学关系推导出位置关系即可)
  3. 注意寻找数字关系
    • 安卓图案解锁 中的键盘数字关系(这题挺难找的)
    • [NOIP1999]回文数 中的进制转换,进制进位(这个较简单)
    • [NOIP2016]玩具谜题 中的圆形容器数字关系(res=(res+n)%n)
  4. Captcha Cracker 这种较为简单的题目,请不要多考虑,直接写就行了,不需要参照上面的总结条例,也不需要自己想复杂。
本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到