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


    模拟

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

    练习

    [蓝桥杯 2020 省 A1] 超级胶水

    小明有 n n n 颗石子,按顺序摆成一排,他准备用胶水将这些石子粘在一起。

    每颗石子有自己的重量,如果将两颗石子粘在一起,将合并成一颗新的石子,重量是这两颗石子的重量之和。

    为了保证石子粘贴牢固,粘贴两颗石子所需要的胶水与两颗石子的重量乘积成正比,本题不考虑物理单位,认为所需要的胶水在数值上等于两颗石子重量的乘积。

    每次合并,小明只能合并位置相邻的两颗石子,并将合并出的新石子放在原来的位置。

    现在,小明想用最少的胶水将所有石子粘在一起,请帮助小明计算最少需要多少胶水。

    输入格式

    输入的第一行包含一个整数 n n n,表示初始时的石子数量。

    第二行包含 n n n 个整数 w 1 , w 2 , ⋯   , w n w_1, w_2, \cdots, w_n w1,w2,,wn 依次表示每颗石子的重量。

    输出格式

    输出一行包含一个整数,表示最少需要的胶水数。

    样例输入 #1

    3
    3 4 5
    
    • 1
    • 2

    样例输出 #1

    47
    
    • 1

    样例输入 #2

    8
    1 5 2 6 3 7 4 8
    
    • 1
    • 2

    样例输出 #2

    546
    
    • 1

    提示

    对于 20 % 20\% 20% 的评测用例, 1 ≤ n ≤ 15 1 \le n \le 15 1n15

    对于 60 % 60\% 60% 的评测用例, 1 ≤ n ≤ 100 1\leq n \leq 100 1n100

    对于 80 % 80\% 80% 的评测用例, 1 ≤ n ≤ 1000 1\leq n \leq 1000 1n1000

    对于所有评测用例, 1 ≤ n ≤ 1 0 5 1\leq n \leq 10^5 1n105 1 ≤ w i ≤ 1000 1 \leq w_i \leq 1000 1wi1000

    蓝桥杯 2020 第一轮省赛 A 组 I 题。

    思路

    • 一开始以为要动态规划,但是自己在推状态转移方程的时候发现,粘石头的顺序什么的都对结果没有影响。就直接暴力模拟即可

    题解

    #include
    #define ri register int
    #define ll long long
    using namespace std;
    ll n,w[100005],sum,ans=0;
    int main() {
    	cin>>n;
    	for(ri i=1;i<=n;i++)
    		cin>>w[i];
    	int sum=w[1];
    	for(ri i=2;i<=n;i++){
    		ans+=sum*w[i];
    		sum+=w[i];
    	}
    	cout<<ans;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    [蓝桥杯 2019 省 A] 外卖店优先级

    “饱了么”外卖系统中维护着 N N N 家外卖店,编号 1 1 1 N N N。每家外卖店都有一个优先级,初始时 ( 0 (0 (0 时刻)优先级都为 0 0 0

    每经过 1 1 1 个时间单位,如果外卖店没有订单,则优先级会减少 1 1 1,最低减到 0 0 0;而如果外卖店有订单,则优先级不减反加,每有一单优先级加 2 2 2

    如果某家外卖店某时刻优先级大于 5 5 5,则会被系统加入优先缓存中;如果优先级小于等于 3 3 3,则会被清除出优先缓存。

    给定 T T T 时刻以内的 M M M 条订单信息,请你计算 T T T 时刻时有多少外卖店在优先缓存中。

    输入格式

    第一行包含 3 3 3 个整数 N N N M M M T T T

    以下 M M M 行每行包含两个整数 t s ts ts i d id id,表示 t s ts ts 时刻编号 i d id id 的外卖店收到。

    一个订单。

    输出格式

    输出一个整数代表答案。

    样例输入 #1

    2 6 6
    1 1
    5 2
    3 1
    6 2
    2 1
    6 2
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    样例输出 #1

    1
    
    • 1

    提示

    样例解释

    6 6 6 时刻时, 1 1 1 号店优先级降到 3 3 3,被移除出优先缓存; 2 2 2 号店优先级升到 6 6 6

    加入优先缓存。所以是有 1 1 1 家店 ( 2 (2 (2 号)在优先缓存中。

    评测用例规模与约定

    对于 80 % 80\% 80% 的评测用例, 1 ≤ N , M , T ≤ 10000 1 \le N,M,T \le 10000 1N,M,T10000

    对于所有评测用例, 1 ≤ N , M , T ≤ 1 0 5 1 \le N,M,T \le 10^5 1N,M,T105 1 ≤ t s ≤ T 1 \le ts \le T 1tsT 1 ≤ i d ≤ N 1 \le id \le N 1idN

    蓝桥杯 2019 年省赛 A 组 G 题。

    思路

    • 看完题目没有想到直接对应的算法,所以就用模拟的思路去写。但是我自己写的时候疏忽了题目的意思,误以为 T T T一直是最终时间,所以只得了 10 10 10优化时间复杂度的方法:根据目前我的做题记录来看主要是多开数组(或者是pair,map),并查集和枚举算法的常用化简
    • 因为题目最大的样例达到 1 0 5 10^5 105,所以时间复杂度要 < O ( n 2 ) <O(n^2) O(n2)(但是,真的用到了 O ( n 2 ) O(n^2) O(n2)好像也能过)

    题解

    #include 
    using namespace std;
    const int N=1e5+5;
    struct node{
        int ts,num;
    }a[N];
    bool cmp(node s1,node s2){
        if(s1.ts==s2.ts)return s1.num<s2.num;
        return s1.ts<s2.ts;
    }
    int n,m,t,ans;
    int last[N],sum[N];
    bool st[N];
    int main(){
        int tt=0;
        scanf("%d%d%d",&n,&m,&t);
        for(int i=1;i<=m;i++)scanf("%d%d",&a[i].ts,&a[i].num);
        sort(a+1,a+1+m,cmp);
        for(int i=1;i<=m;i++){
            int tt,id;
            tt=a[i].ts;id=a[i].num;
            if(tt!=last[id])sum[id]-=tt-last[id]-1;
            if(sum[id]<0)sum[id]=0;
            if(sum[id]<=3)st[id]=0;
            sum[id]+=2;
            if(sum[id]>5)st[id]=1;
            last[id]=tt;
        }
        for(int i=1;i<=n;i++){
            if(last[i]<t)sum[i]-=t-last[i];
            if(sum[i]<=3)st[i]=0;
        }
        for(int i=1;i<=n;i++) if(st[i])ans++;
        cout<<ans<<endl;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    [蓝桥杯 2020 省 B1] 整数拼接

    给定一个长度为 n n n 的数组 A 1 , A 2 , ⋯   , A n A_1,A_2,\cdots,A_n A1,A2,,An。你可以从中选出两个数 A i A_i Ai A j A_j Aj i ≠ j i\neq j i=j),然后将 A i A_i Ai A j A_j Aj 一前一后拼成一个新的整数。例如 12345 可以拼成 1234534512。注意交换 A i A_i Ai A j A_j Aj 的顺序总是被视为 2 2 2 种拼法,即便是 A i = A j A_i=A_j Ai=Aj 时。

    请你计算有多少种拼法满足拼出的整数是 K K K 的倍数。

    输入格式

    第一行包含 2 2 2 个整数 n n n K K K

    第二行包含 n n n 个整数 A 1 , A 2 , ⋯   , A n A_1,A_2,\cdots,A_n A1,A2,,An

    输出格式

    一个整数代表答案。

    样例输入 #1

    4 2
    1 2 3 4
    
    • 1
    • 2

    样例输出 #1

    6
    
    • 1

    提示

    对于所有评测用例, 1 ≤ n ≤ 1 0 5 1\le n\le10^5 1n105 1 ≤ k ≤ 1 0 5 1\le k\le10^5 1k105 1 ≤ A i ≤ 1 0 9 1\le A_i\le10^9 1Ai109

    蓝桥杯 2020 第一轮省赛 B 组 I 题。

    思路

    • 因为这道题与整数小拼接非常相似,我就以为方法也类似。所以先从尺取法思考,但是发现端点不能决定区间剩余元素的结果。
    • n n n最大为 1 0 5 10^5 105,所以时间复杂度 O ( n 2 ) O(n^2) O(n2)无法通过

    题解

    #include
    using namespace std;
    long long a[11][100004];
    long long s[100004];
    int wei(long long n){
    	int ans=0;
    	while(n){
    		ans++;
    		n=n/10;
    	}
    	return ans;
    }
    int main(){
    	int n,k;
    	cin>>n>>k;
    	for (int i=0;i<n;i++)	cin>>s[i];
    	for(int j=1;j<10;j++){
    		for(int i=0;i<n;i++){
    			long long result=(long long)pow(10,j)*s[i]%k;
    			a[j][result%k]++;	
    		}
    	}
    	long long ans=0;
    	for(int i=0;i<n;i++){
    		long long w=wei(s[i]);
    		int flag=k-s[i]%k;
    		if(flag==k)flag=0;
    		ans=ans+a[w][flag];
    		if((s[i]*(long long)pow(10,w)%k+s[i]%k)%k==0)ans--;
    	}
    	cout<<ans;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    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 
    
    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;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    [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 
    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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    [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 
    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
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    总结

    1. 模拟类型的题目时,首先跟着题目意思,把题目读懂,按照题目要求即可,不需要预先建模。
    2. 如果可以用已知的代码知识一比一模拟出题目效果最佳(如前文中的安卓图案解锁 直接可以模拟出9位键盘),但如果没找到很契合的代码知识时,不要强求一比一模拟(如:[NOIP2016]玩具谜题 这种,仅凭我目前的代码知识来看,无法找到一个真正的圆形的容器,容纳玩具。但是用2个数组,一个存方向,一个存名字,再根据数学关系推导出位置关系即可)
    3. 注意寻找数字关系
      • 安卓图案解锁 中的键盘数字关系(这题挺难找的)
      • [NOIP1999]回文数 中的进制转换,进制进位(这个较简单)
      • [NOIP2016]玩具谜题 中的圆形容器数字关系(res=(res+n)%n)
    4. Captcha Cracker 这种较为简单的题目,请不要多考虑,直接写就行了,不需要参照上面的总结条例,也不需要自己想复杂。
  • 相关阅读:
    智能家居2.0 - Matter 1.0 标准和受益者
    对NFT许可的观察:事实与虚构
    TuckER 论文笔记
    Java_自定义实体类的列表List<T>调用remove()失败讲解
    解锁Mysql中的JSON数据类型,怎一个爽字了得
    牛视系统源码,抖音矩阵系统功能开发定制。I‘m here
    不止跑路,拯救误操作rm -rf /*的小伙儿
    GitHub上最全的Java面试题库竟还要收费?黑客强行开源后遭起诉
    Java HashSet集合概述
    (Spark Connect)使用Aerospike的 Spark 连接器
  • 原文地址:https://blog.csdn.net/qq_61786525/article/details/126368257