buct2024寒假集训 进阶训练赛 (一)题解

目录

问题 A: 校门外的树

题目描述

输入

输出

样例输入

样例输出

思路分析 

代码详细


问题 A: 校门外的树

题目描述

某校大门外长度为L的马路上有一排树,每两棵相邻的树之间的间隔都是1米。我们可以把马路看成一个数轴,马路的一端在数轴0的位置,另一端在L的位置;数轴上的每个整数点,即0,1,2,……,L,都种有一棵树。

由于马路上有一些区域要用来建地铁。这些区域用它们在数轴上的起始点和终止点表示。已知任一区域的起始点和终止点的坐标都是整数,区域之间可能有重合的部分。现在要把这些区域中的树(包括区域端点处的两棵树)移走。你的任务是计算将这些树都移走后,马路上还有多少棵树。

输入

第一行有两个整数L(1 ≤ L ≤ 10000)和 M(1 ≤ M ≤ 100),L代表马路的长度,M代表区域的数目,L和M之间用一个空格隔开。接下来的M行每行包含两个不同的整数,用一个空格隔开,表示一个区域的起始点和终止点的坐标。

对于20%的数据,区域之间没有重合的部分;对于其它的数据,区域之间有重合的情况。

输出

包括一行,这一行只包含一个整数,表示马路上剩余的树的数目。

样例输入
500 3
150 300
100 200
470 471
样例输出
298
思路分析 

定义一个长度为L+1的数组,用0判断是否有树,有为0,所征地处区间均累加,统计0的个数;

代码详细
#include<iostream>
#include<bits/stdc++.h>
using namespace std;
int main(){
    int L,M;
    cin>>L>>M;
    int al[L+1],am[M][2];
    int i,j;
    for(i=0;i<=L;i++){
        al[i]=0;
    }
    for(i=0;i<M;i++)
    {
        cin>>am[i][0]>>am[i][1];
        for(j=am[i][0];j<=am[i][1];j++)
        {
            al[j]++;
        }
    }
    int sum=0;
    for(i=0;i<=L;i++){
        if(al[i]==0)sum++;
    }
    cout<<sum<<endl;
}

问题 B: 开关灯

题目描述

假设有N盏灯(N为不大于5000的正整数),从1到N按顺序依次编号,初始时全部处于开启状态;有M个人(M为不大于N的正整数)也从1到M依次编号。
 

第一个人(1号)将灯全部关闭,第二个人(2号)将编号为2的倍数的灯打开,第三个人(3号)将编号为3的倍数的灯做相反处理(即将打开的灯关闭,将关闭的灯打开)。依照编号递增顺序,以后的人都和3号一样,将凡是自己编号倍数的灯做相反处理。
 

请问:当第M个人操作之后,哪几盏灯是关闭的,按从小到大输出其编号,其间用逗号间隔。

输入

输入正整数N和M,以单个空格隔开。

输出

顺次输出关闭的灯的编号,其间用逗号间隔。

样例输入 
10 10
样例输出 
1,4,9
思路分析 

第一次和第二次开关灯和其余次数不同,分开处理;

第三次后按照0、1进行开关灯处理;

最后统计输出0(关着的灯);

代码详细
#include<iostream>
#include<bits/stdc++.h>
using namespace std;
int main(){
    int N,M;
    cin>>N>>M;
    int a[N+1];
    if(M==0)return 0;
    if(M>=1){
        for(int i=0;i<=N;i++)
        {
            a[i]=0;
        }
    }
    if(M>=2){
        for(int i=1;i*2<=N;i++)
        {
            a[i*2]=1;
        }
    }
    for(int i=3;i<=M;i++)
    {
        for(int j=1;i*j<=N;j++){
            if(a[i*j]==1){a[i*j]=0;}
            else {a[i*j]=1;}
        }
    }
    int p=0;
    for(int i=1;i<=N;i++)
    {
        if(a[i]==0){
            if(p)cout<<",";
            cout<<i;
            p=1;
        }
    }
}

问题 C: 机器翻译

题目描述

小晨的电脑上安装了一个机器翻译软件,他经常用这个软件来翻译英语文章。

这个翻译软件的原理很简单,它只是从头到尾,依次将每个英文单词用对应的中文含义来替换。对于每个英文单词,软件会先在内存中查找这个单词的中文含义,如果内存中有,软件就会用它进行翻译;如果内存中没有,软件就会在外存中的词典内查找,查出单词的中文含义然后翻译,并将这个单词和译义放入内存,以备后续的查找和翻译。

假设内存中有M 个单元,每单元能存放一个单词和译义。每当软件将一个新单词存入内存前,如果当前内存中已存入的单词数不超过M−1,软件会将新单词存入一个未使用的内存单元;若内存中已存入M 个单词,软件会清空最早进入内存的那个单词,腾出单元来,存放新单词。

假设一篇英语文章的长度为N 个单词。给定这篇待译文章,翻译软件需要去外存查找多少次词典?假设在翻译开始前,内存中没有任何单词。

输入

共2 行。每行中两个数之间用一个空格隔开。

第一行为两个正整数M 和N,代表内存容量和文章的长度。

第二行为N 个非负整数,按照文章的顺序,每个数(大小不超过1000)代表一个英文

单词。文章中两个单词是同一个单词,当且仅当它们对应的非负整数相同。

【数据范围】

对于10%的数据有M=1,N≤ 5。

对于100%的数据有0 < M≤ 100,0< N ≤ 1000。
 

输出

    共1 行,包含一个整数,为软件需要查词典的次数。

样例输入 
3 7
1 2 1 5 4 4 1
样例输出 
5
思路分析 

本题为查找外词典的次数,也就是存储的词典找不到该词汇的次数;

定义一个数组作为存储词汇的工具,定义一个p,在数组内能查到p=1,反之p=0;

用栈的原理,不能查到的情况判断数组是否满编,是则删除第一位并将后面的向前挪一位再入数组,否则直接入数组。

代码详细
#include <bits/stdc++.h>
#include <iostream>
using namespace std;
int main() {
    int M, N;
    cin >> M >> N;
    int i, a[N], b[M], t = 0, sum = 0;
    for (i = 0; i < N; i++) {
        cin >> a[i];
        int p = 0;
        for (int j = 0; j < t; j++) {
            if (b[j] == a[i]) {
                p = 1;
                break;
            }
        }
        if (p == 0) {
            sum++;
            if (t <= M - 1) {
                b[t] = a[i];
                t++;
            } else {
                for (int k = 0; k < M - 1; k++) {
                    b[k] = b[k + 1];
                }
                b[M - 1] = a[i];
            }
        }
    }
    cout << sum << endl;
}

问题 D: 素数对

题目描述

两个相差为2的素数称为素数对,如5和7,17和19等,本题目要求找出所有两个数均不大于n的素数对。

输入

一个正整数n(1≤n≤10000)。

输出

所有小于等于n的素数对。每对素数对输出一行,中间用单个空格隔开。若没有找到任何素数对,输出empty。

样例输入 复制
100
样例输出 复制
3 5
5 7
11 13
17 19
29 31
41 43
59 61
71 73
思路分析 

因为最小的素数对是3和5,在n的范围内从5起对每个数 i 进行素数判断,若是则判断 i + 2 是否为素数,若是则是一对素数对,n小于5则直接empty;

代码详细
#include <bits/stdc++.h>
#include <iostream>
#include <math.h>
using namespace std;
bool zhi(int a){
    int i;
    for(i=2;i<=sqrt(a);i++){
        if(a%i==0){return false;}
    }
    return true;
}
int main() {
    int n;
    cin>>n;
    if(n<5){cout<<"empty"<<endl;return 0;}
    else {
        for(int i=3;i<=n-2;i++){
            if(zhi(i)&&zhi(i+2))cout<<i<<" "<<i+2<<endl;
        }
    }
}

问题 E: 阿克曼函数

题目描述

阿克曼(Ackmann)函数A(m,n)中,m,n定义域是非负整数(m≤3,n≤10),函数值定义为:
 

输入

输入m和n。

输出

函数值

样例输入
2 3
样例输出
9
思路分析 

运用函数的迭代,按照所给函数编造函数得出答案即可;

代码详细
#include <bits/stdc++.h>
#include <iostream>
#include <math.h>
using namespace std;
int akm(int m,int n){
    if(m==0)return n+1;
    else if(n==0)return akm(m-1,1);
    else return akm(m-1,akm(m,n-1));
}
int main() {
    int m,n;
    cin>>m>>n;
    cout<<akm(m,n)<<endl;
}

问题 F: 甲流病人初筛

题目描述

目前正是甲流盛行时期,为了更好地进行分流治疗,医院在挂号时要求对病人的体温和咳嗽情况进行检查,对于体温超过37.5度(含等于37.5度)并且咳嗽的病人初步判定为甲流病人(初筛)。现需要统计某天前来挂号就诊的病人中有多少人被初筛为甲流病人。

输入

第一行是某天前来挂号就诊的病人数n。(n<200)
 

其后有n行,每行是病人的信息,包括三个信息:姓名(字符串,不含空格,最多8个字符)、体温(float)、是否咳嗽(整数,1表示咳嗽,0表示不咳嗽)。每行三个信息之间以一个空格分开。

输出

按输入顺序依次输出所有被筛选为甲流的病人的姓名,每个名字占一行。之后在输出一行,表示被筛选为甲流的病人数量。

样例输入
5
Zhang 38.3 0
Li 37.5 1
Wang 37.1 1
Zhao 39.0 1
Liu 38.2 1
样例输出
Li
Zhao
Liu
3
思路分析 

用结构体分别存储姓名、体温和是否咳嗽,存储过程中判断体温是否大于37.5度以及是否咳嗽,若判断初筛为甲流,则输出姓名即可;

代码详细
#include <bits/stdc++.h>
#include <iostream>
#include <math.h>
using namespace std;
struct chushai{
    char xm[20];
    float tw;
    int ks;
}s[200];
int main() {
    int n,sum=0;
    cin>>n;
    for(int i=0;i<n;i++){
        cin>>s[i].xm>>s[i].tw>>s[i].ks;
        if(s[i].tw>=37.5&&s[i].ks==1){
            sum++;
            cout<<s[i].xm<<endl;
        }
    }
    cout<<sum<<endl;
}

问题 G: 【蓝桥杯2021初赛】空间

题目描述

小蓝准备用256MB 的内存空间开一个数组,数组的每个元素都是32 位二进制整数。
如果不考虑程序占用的空间和维护内存需要的辅助空间,请问256MB 的空间可以存储多少个32 位二进制整数?

思路分析 

1MB=1024KB,1KB=1024B

答案

67108864

问题 H: 【蓝桥杯2021初赛】卡片

题目描述

小蓝有很多数字卡片,每张卡片上都是数字0 到9。
小蓝准备用这些卡片来拼一些数,他想从1 开始拼出正整数,每拼一个,就保存起来,卡片就不能用来拼其它数了。
小蓝想知道自己能从1 拼到多少。
例如,当小蓝有30 张卡片,其中0 到9 各3 张,则小蓝可以拼出1 到10,但是拼11 时卡片1 已经只有一张了,不够拼出11。
现在小蓝手里有0 到9 的卡片各2021 张,共20210 张,请问小蓝可以从1拼到多少?

思路分析 

定义一个0—9的数组,把每个数所用的卡片从数组中减去,直至一个数无法被表示,也就是卡片为0;

代码详细
#include <bits/stdc++.h>
#include <iostream>
#include <math.h>
using namespace std;
int main() {
    int n = 0;
    int a[11];
    for (int i = 0; i <= 9; i++) {
        a[i] = 2021;
    }
    for (int j = 1; j <= 5000; j++) {
        int y = j;
        while (y > 0) {
            int s = y % 10;
            a[s]--;
            y = y / 10;
        }
        n++;
        for (int k = 0; k <= 9; k++) {
            if (a[k] == 0) {
                printf("%d", n);
                return 0;
            }
        }
    }
}

问题 I: 回到学校

题目描述

愉快的假期总是那么的短暂。

回到学校后,小T监测了教室的进出情况。

假设班级里有 N 个同学,学号分别是 1 ∼ N ,小T记录了每位同学分别是第几个进入的教室。

你可以通过上述记录,按进入教室的先后顺序输出同学的学号吗?

输入
N
A1 A2 ... AN

第一行一个正整数 N NN ,代表同学的个数。

第二行有空格隔开的 N NN 个正整数,A i A_iAi 代表学号为 i ii 的同学是第几个进入教室的。

数据范围:

  • 1 ≤ N ≤ 10^5
  • 1 ≤ Ai ≤ N
  • A i ≠ A j ( i ≠ j )
输出

输出一行一个正整数,第 i 个整数代表第 i 个进入教室的同学的学号。

样例输入 复制
3
2 3 1
样例输出 复制
3 1 2
思路分析 

用pair数组来分别存储次序和学号,然后按照次序排序(注意此题时间复杂度O(n),容易超时)

pair的用法见下

https://blog.csdn.net/gzplyx/article/details/129360315?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522170538414216800180640764%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=170538414216800180640764&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-1-129360315-null-null.142^v99^pc_search_result_base9&utm_term=pair%3Cint%20int%3E&spm=1018.2226.3001.4187

代码详细
#include <bits/stdc++.h>
#include <iostream>
#include <math.h>
using namespace std;
pair<int, int> a[100010];
int main() {
    int n;
    cin>>n;
    for(int i=0;i<n;i++){
        cin>>a[i].first;
        a[i].second=i+1;
    }
    sort(a,a+n);
    for(int i=0;i<n;i++){
        cout<<a[i].second<<" ";
    }
}

 问题 J: 九九乘法表(教师版)

题目描述

你是XSY的老师,XSY只学过九九乘法表,你看到他的作业中出现了一个数n。

如果这个数在九九乘法表是找不到的,那么他肯定答错了,就输出"No"(不含引号),否则,就输出"Yes"(不含引号)
 

输入

一个整数 n
代表XSY的答案。 (1≤n≤100)

输出

输出 Yes 或 No

样例输入
10
样例输出
Yes
输入样例2
11
输出样例2
No
思路分析 

利用bool函数,将乘法表内的数都标记为true,反之为false,然后进行查找即可;

代码详细
#include <bits/stdc++.h>
#include <iostream>
#include <math.h>
using namespace std;
bool cf[101];
int main() {
    int n;
    cin>>n;
    for(int i=0;i<101;i++){
        cf[i]=false;
    }
    for(int i=1;i<10;i++){
        for(int j=1;j<10;j++){
            cf[i*j]=true;
        }
    }
    if(cf[n])cout<<"Yes"<<endl;
    else cout<<"No"<<endl;
}

问题 K: 好人 or 坏人 ?

题目描述

有n个人在玩一个游戏,
这个游戏是这样的,我们将n个人分为好人和坏人,也有可能n个人都是好人或者都是坏人
好人说的话一定是对的,坏人说的话有可能对,也有可能不对。
现在给你每个人对另外一些人的判断,
问好人最多有多少个

输入

第一行输入n , 表示有n个人
接下来若干行 , 每一行输入一个Ai
Ai表示 i 这个人说了Ai条信息,
接下来Ai行,每一行输入一个Xi , Yi
如果Yi = 1
表示 i 这个人认为Xi这个人是好人
如果Yi= 0
表示 i 这个人认为Xi这个人是坏人
1 <= n <= 15
0 <= Ai <= n - 1
1 <=  Xi <= n 
0 <= Yi <= 1
 

输出

好人最多有多少个

样例输入 
3
1
2 1
1
1 1
1
2 0
样例输出 
2
思路分析 

利用二进制化的方案编号用1或0表示某位人是好人还是坏人,如三个人时第四种方案4的二进制为100,代表第一个人是好人,第二第三是坏人。

bit1Count这个函数是用来统计好人数也就是二进制化后 1 的个数;

在主函数main中输入谁认为谁好坏我们用行代表人,用列代表该人认为其他人如何建立一个二维数组,用1表示好人,0表示坏人,2表示没有对其表述;

二维数组的指针法表示详见

https://blog.csdn.net/BeerBread134/article/details/79733237

maximumGood函数中判断如果方案二进制化的好坏人判断是否和上述二维数组相匹配,从而得出最多的好人数。

代码详细
#include <bits/stdc++.h>
#include <iostream>
#include <math.h>
using namespace std;
int isGoodPeople(int state, int x) { return state >> x & 1; }

int max(int a, int b) { return a > b ? a : b; }

int bit1Count(int x) {
    int cnt = 0;
    while (x) {
        x &= (x - 1);
        ++cnt;
    }
    return cnt;
}

int maximumGood(int** statements, int n) {
        int i, j, k;
    int flag;
    int ret = 0;
    for(i = 0; i < (1<<n); ++i) {
        flag = 0;
        for(j = 0; j < n; ++j) {
            if( isGoodPeople(i, j) ) {
                // i这种方案下,j 是个好人
                for(k = 0; k < n; ++k) {
                    if(statements[j][k] == 2) {
                        continue; 
                    }
                    if(statements[j][k] != isGoodPeople(i, k)) {
                        flag = 1;
                        break;
                    }
                }
                if(flag) {
                    break;
                }
            }else {
                // i这种方案下,j 是个坏人,不处理该数据;
            }
        }
        if(!flag) {
            ret = max(ret, bit1Count(i));
        }
    }
    return ret;
}
int main() {
    int n;
    cin >> n;
    int q = n;
    int** statements = new int*[n];
    for (int i = 0; i < n; i++)
        statements[i] = new int[n];
    int aq[n][n];
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            aq[i][j] = 2;
        }
    }
    while (q) {
        int a;
        cin>>a;
        for (int i = 0; i < a; i++) {
            int t1, t2;
            cin >> t1 >> t2;
            aq[n - q][t1 - 1] = t2;
        }
        q--;
    }
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            statements[i][j]=aq[i][j];
        }
    }
    cout << maximumGood(statements, n) << endl;
}

问题 L: 双刀流银色战车

题目描述

银色战车+阿努比斯神是波波仅有的高光时刻,那么波鲁纳雷夫玩节奏光剑的话会怎么样呢?
给出两个长度均为n字符串,L表示所有的左手键需要砍的字符,R表示所有的右手键需要砍的字符。
波波挥剑时遵循左-右-左-右……的顺序。
那么字符出现的顺序应该如何?

输入

1≤n≤100
|L|==|R|==n

输出

总的字符串

样例输入
2
ip cc
样例输出
icpc
思路分析 

交叉输出即可。

代码详细
#include <bits/stdc++.h>
#include <iostream>
#include <math.h>
using namespace std;
int main(){
    int n;
    cin>>n;
    char a[n],b[n];
    cin>>a>>b;
    for(int i=0;i<n;i++){
        cout<<a[i]<<b[i];
    }
    cout<<endl;
}

问题 M: 点外卖

题目描述

小L和朋友们想趁着疯狂星期八薅羊毛。
他们一共点了n样,每样的价钱是Ai(1≤ i ≤ n)。
疯狂星期八的优惠措施是这样的:店家会给你m张优惠劵,优惠券的作用是将某样的价格打对折(下取整),并且它是可以叠加的。
那么小L他们最少需要多少钱。

输入

n道菜,m张券,第i道菜的价格是x。
1≤ n,m ≤105
1≤ x ≤109

输出

最少要花多少钱

样例输入 复制
4 4
1 9 3 5
样例输出 复制
6
思路分析 

利用优先队列处理数据,不断对顶部进行打折;

优先队列详见

https://blog.csdn.net/weixin_36888577/article/details/79937886?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522170538679516800186594105%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=170538679516800186594105&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_positive~default-1-79937886-null-null.142^v99^pc_search_result_base9&utm_term=%E4%BC%98%E5%85%88%E9%98%9F%E5%88%97&spm=1018.2226.3001.4187

代码详细
#include <bits/stdc++.h>
#include <iostream>
#include <math.h>
using namespace std;
int main() {
    int n, m;
    cin >> n >> m;
    priority_queue<int> q;
    for (int i = 0; i < n; i++) {
        int t;
        cin >> t;
        q.push(t);
    }
    while (m--) {
        int t = q.top();
        t /= 2;
        q.pop();
        q.push(t);
    }
    long long sum = 0;
    while (q.size()) {
        sum += q.top();
        q.pop();
    }
    cout << sum << endl;
}

 下面的题目未完成转载

        问题 N: 宝藏开箱者

        https://letmefly.blog.csdn.net/article/details/123415471

        问题 O: 奇异吃牌者

   2021-2022-2 ACM集训队每周程序设计竞赛(2) - 问题 F: 奇异吃牌者 - 题解_有一个性格奇异的人,喜欢吃掉不同的扑克牌 初始时共有 n nn 张牌,第 i ii 张牌上-CSDN博客

        问题 P: 2.4.9.2 双重队列

【POJ No. 3481】双重队列 Double Queue-CSDN博客

        问题 Q: 第K小乘积[LeetCode解题报告] 2040. 两个有序数组的第 K 小乘积_双数组求第k小是leetcode哪道题-CSDN博客
        问题 R: 消灭大整数

        解:判断下某位数直接变为0次数少还是进一位再消除的次数少即可

        问题 S: 关灯

        无解放个题目

        问题 T: 计信狗的大一下

        同上

  • 33
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值