【算法比赛】竞码编程-蓝桥杯校内选拔赛(初赛)重现赛

怎么说呢,就是一个信心赛,哈哈哈,打着给自己信心的,这种偶尔做做,抚慰一下菜鸡的内心,还是可以的。(但不能沉迷在简单题的基础,还是要不断错,不断学习的)

 

比赛:

http://oj.hzjingma.com/contest/problem?id=72


试题A:第N个素数(暴力枚举)

试题B:coffee的签到题(规律题)

试题C:不一样的日期(模拟)

试题D:三位分节法(模拟)

试题E:水坑题(取模运算)

试题F:站队(排序 + 结构体)

试题G:冷门进制(思维 + 因子2,3 的个数)

试题H:拯救阿拉德大陆(容斥原理)

试题I:切香肠(二分)

试题J:馋嘴羊(BFS)


试题A:第N个素数(暴力枚举)

 

要求我们输入,第 300 个素数是多少(从 2 3 5 开始...),直接暴力枚举即可。把在一定范围内的所有素数找出来,然后数第 300 个是多少即可

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 1e5;
int p[MAXN];

void init()
{
    memset(p, 0, sizeof(p));
    for(int i = 2;i < MAXN; ++i)
    {
        if(p[i] == 0)
        {
            for(int j = 2 * i; j < MAXN; j += i)
            {
                p[j] = 1;
            }
        }
    }
}

int main()
{
    init();
    int cnt = 0;
    for(int i = 2;i < MAXN; ++i)
    {
        if(p[i] == 0) ++cnt;
        if(cnt == 300)
        {
            cout << i << endl;
            break;
        }
    }
    return 0;
}

 


试题B:coffee的签到题(规律题)

 

自己计算 4 5 6 7 8 9 的情况,发现,奇数就是 yes,偶数就是 no

这里要注意的一个地方,输入的数 n,可能是 10 ^ (100) 大小,因此无法用 long long 存储,那么只能用字符串输入。然后判断奇偶性的时候,相当于把字符串看成一个数,直接判断最后一位即可(把字符串最后一位变成 数)。

 

#include <bits/stdc++.h>
using namespace std;

int main()
{
    string s;
    cin >> s;

    int val = s[s.size() - 1] - '0';
 
    if(val % 2 == 1) cout << "yes" << endl;
    else cout << "no" << endl;
    return 0;
}

 


试题C:不一样的日期(模拟)

 

这道题主要就是模拟,根据输入的 d ,是 > 0 还是 < 0 分开情况模拟。、

假设经过了 d 天,我们先得到 相当于是经过了,几年,几月,几日,然后原来的年月日,加上 或者 减去。这个时候,要判断,日会不会是 <= 0 或者 > 13(这样子就相当于要从月份中,借位或者进位),同样的,月会不会 <= 0 或者 > 23 (这样子就相当于要从年份中,借位或者进位)。

总体而言,就是一道模拟题,对应处理即可。

 

#include <bits/stdc++.h>
using namespace std;

int main()
{
    // 23 个月,13天
    int Y, M, D, d;
    cin >> Y >> M >> D >> d;
    if(d > 0)
    {
        int y = d / (23 * 13);
        d = d % (23 * 13);
        int m = d / 13;
        d = d % 13;
        Y += y;
        M += m;
        D += d;
        while(D > 13)
        {
            D -= 13;
            ++M;
        }
        while(M > 23)
        {
            M -= 23;
            ++Y;
        }
    }
    else if(d < 0)
    {
        d = -d;
        int y = d / (23 * 13);
        d = d % (23 * 13);
        int m = d / 13;
        d = d % 13;
        Y -= y;
        M -= m;
        D -= d;
        while(D <= 0)
        {
            D += 13;
            --M;
        }
        while(M <= 0)
        {
            M += 23;
            --Y;
        }
    }
    printf("%d %d %d\n", Y, M, D);
    return 0;
}

 


试题D:三位分节法(模拟)

 

就是按照输入的数,三个三个之间加入逗号,而且输入的数很大,所以直接用字符串存,同时输出也用字符串存。

那么处理的时候,从前往后处理,后面的都是 3 个 3 个一起的,所以一开始,先取 3 的模,如果不为 0,说明前面多东西,比如 1000,应该结果是 1, 000。这样子先处理 取 3 的模结果的前几个数。

这里要注意,可能数是 23  那么结果应该是 23  而不能是 23,    

所以在把前面几个多余的处理后,要不要加 ,   就要考虑后面还有没有数

然后剩下的后面,都是 3 个 3 个 一组,先把 3 个数输出后 再加上 ,   

这里要注意的就是 234, 234  也就是说,最后的 3 个输出后 是不加上 , 的   所以要注意了

因此这道题是一道模拟题,主要就是要注意一些点即可。

 

#include <bits/stdc++.h>
using namespace std;

int main()
{
    string str;
    cin >> str;
    int len = str.size();

    string res = "";
    int i = 0;
    if(len % 3 != 0)  // 先把前面多的,处理
    {
        for(;i < (len % 3); ++i)
        {
            res += str[i];
        }
        if(i != len) res += ",";  // 要不要加,就是看,后面还有没有数
    }
    
    int cnt = 0;
    for(;i < len; ++i)
    {
        ++cnt;
        res += str[i];
        if(cnt == 3 && i != len - 1)  // 三个一组后,要加上逗号,但是要求,最后一组的时候,后面是不跟逗号的
        {
            res += ",";
            cnt = 0;
        }
    }
    cout << res << endl;
    return 0;
}

试题E:水坑题(取模运算)

简单题,根据输入的 a b 数据大小,用 long long 存,同时根据取模的运算 (a * b) % MOD = (a % MOD) * (b % MOD) % MOD。

 

#include <bits/stdc++.h>
using namespace std;

const long long MOD = 1e9 + 7;

int main()
{
    long long a, b;
    cin >> a >> b;
    long long res = ((a % MOD) * (b % MOD)) % MOD;
    cout << res << endl;
    return 0;
}

 


试题F:站队(排序 + 结构体)

 

根据题意,一个学生有三个性质,因此想到用结构体来存储。然后要根据三个性质,对学生进行排序

典型的排序问题,直接重写 sort 函数中 cmp 即可(用结构体的三个值来进行判断)

 

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 1e6 + 15;

struct Stu  // 结构体
{
    int H;
    int D;
    int number;
};

Stu stu[MAXN];  // 学生数组

bool cmp(Stu a, Stu b)   // 结构体排序
{
    // 三个排序规则
    if(a.H != b.H) return a.H < b.H;
    if(a.D != b.D) return a.D < b.D;
    return a.number < b.number;
}

int main()
{
    int N;
    scanf("%d", &N);
    for(int i = 0;i < N; ++i)
    {
        scanf("%d %d", &stu[i].H, &stu[i].D);
        stu[i].number = i + 1;
    }
    sort(stu, stu + N, cmp);
    for(int i = 0;i < N; ++i)
    {
        printf("%d", stu[i].number);
        
        if(i == N-1)
            printf("\n");
        else
            printf(" ");
    }

    return 0;
}

试题G:冷门进制(思维 + 因子2,3 的个数)

 

根据我们要把一个十进制的值,变为 六进制,比如对于 18 ,我们变成 六进制(类似,十进制变成二进制,不断除法取模取商)的过程是: 18 / 6 = 3 .. 0,3 / 6 = 0 ... 3,所以变成六进制是 30。

那么我们发现,要看末尾有几个 0,那就是在一开始 除 6 的时候,是整除的,也就是模为 0。

那么相当于,我们要在所有数相等中,找到因子是 6 的个数,同时,由于 6 = 2 * 3,因此,结果就是,找到所有数中的,因子 2 和 3 的个数。

同时,由于 2 和 3 要各一个,才有一个 6 存在。因此,最后的末尾 0 个数,取决于 因子 2 和 3 中的最少数(因为最少数,意味着,2和3 可以配对起来,多出来的 2 或者 3 无法配对成为 6).

因此,问题,变成了,找打,所有数中 因子 2 和 3 的个数

 

#include <bits/stdc++.h>
using namespace std;

#define LL long long

int main()
{
    int n;
    cin >> n;
    LL two = 0, three = 0;  // 因子 2 和 3 的个数
    LL a;
    for(int i = 0;i < n; ++i)
    {
        cin >> a;
        LL tep = a;
        while(tep % 2 == 0)  // 这个数,不断除 2,得到 因子是 2 的个数
        {
            ++two;
            tep /= 2;
        }

        tep = a;
        while(tep % 3 == 0)  // 同理
        {
            ++three;
            tep /= 3;
        }

    }
    // cout << two << " " << three << endl;
    LL res = min(two, three);
    cout << res << endl;
    return 0;
}

试题H:拯救阿拉德大陆(容斥原理)

 

题目很长,都是废话,重点是:有 1 ~ n ,总共 n 个人,找出能被 a,b,c,d 其中一个整除的所有数的个数。

根据 n / a,其实就是,在 1 ~ n 中,能被 a 整除的个数。

容斥原理的意思是,假设有多个集合 A,B,C,要求,所有集合的并集结果 : A∪B∪C = A+B+C - A∩B - B∩C - C∩A + A∩B∩C。

如果扩展到多个集合,也就是:

那么,这道题,A 区间就是 能被 a 整除的个数,B就是能被 b 整除的个数,C就是 能被 c 整除的,D就是能被 d 整除的。

结果不是单独这几个集合之和,因为会有重复部分,所以是并集关系

这里要注意 A∩B 值得是,能同时被 a 和 b 整除的个数(也就是找出 a 和 b 的最小公倍数)。其他的类似

所以这道题中,还有会求,最小公倍数(利用最大公约数 gcd 来求,最快)

 

#include <bits/stdc++.h>
using namespace std;

#define LL long long 

LL gcd(LL a, LL b)  // 最大公约数
{
    if(b == 0) return a;

    return gcd(b, a % b);
}

LL maxMultiple(LL a, LL b)  // 最小公倍数
{
    LL c = gcd(a, b);
    LL res = (a * b) / c;
    return res;
}

int main()
{
    LL n;
    cin >> n;
    LL a, b, c, d;
    cin >> a >> b >> c >> d;

    LL res = 0;
    res += n / a;
    res += n / b;
    res += n / c;
    res += n / d;

    res -= n / maxMultiple(a, b);
    res -= n / maxMultiple(a, c);
    res -= n / maxMultiple(a, d);
    res -= n / maxMultiple(b, c);
    res -= n / maxMultiple(b, d);
    res -= n / maxMultiple(c, d);

    res += n / maxMultiple(maxMultiple(a, b), c);
    res += n / maxMultiple(maxMultiple(a, b), d);
    res += n / maxMultiple(maxMultiple(a, c), d);
    res += n / maxMultiple(maxMultiple(b, c), d);

    res -= n / maxMultiple(maxMultiple(maxMultiple(a, b), c), d);

    cout << res << endl;

    return 0;
}

 


试题I:切香肠(二分)

 

对可能的长度进行二分查找,然后利用这个长度,去计算 n 个香肠的可以被切成几部分

如果已经满足 k 份,也就是长度可以更大区间里找。如果不满足,说明长度要短一点,去更小区间找。因此可以利用二分,不断的缩小区间

因此,时间复杂度是 O(N * logL),不会超时。

还有注意的一个地方,就是由于这个是有小数,小数有精度,就很麻烦。根据题目,最多精度是 0.01,因此我们可以把所有情况,都增大 100 倍,变成整数来处理,最后得到的长度,再 除100 变回小数。

 

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 1e4 + 15;
int L[MAXN];

int main()
{
    int n, k;
    cin >> n >> k;
    
    for(int i = 0;i < n; ++i)
    {
        double a;
        scanf("%lf", &a);
        L[i] = a * 100;
    }
    
    int left = 0, right = 1e8;
    int res = 0;
    while(left < right)
    {
        int mid = (left + right) / 2;
        int cnt = 0;

        for(int i = 0;i < n; ++i)
        {
            
            cnt += L[i] / mid;
        }
        // cout << left << " " << right << endl;
        // cout << mid << " " << cnt << endl << endl;
        if(cnt >= k)
        {
            res = max(res, mid);
            left = mid + 1;
        }
        else
            right = mid;

    }
    // printf("%d\n", res);
    printf("%0.2lf\n", res / 1.0 / 100);
    return 0;
}

试题J:馋嘴羊(BFS)

 

根据题目,其实就是,从起点,找到可以从这个点,走到的其他点的数量,那么可以考虑用DFS 或者 BFS。但是DFS可能会超时,因为它是每一次都会有 4 种可能,题目总共有 n * m 个点,每个点可能有 4 种DFS下去的情况,那么这样子可能复杂度很大(实际上没那么大,因为一些走过的点,就不会考虑,所以有一些点的不会说有 4 种可能)。(但是当 n m 很大的时候,的确是有超时的可能性)

所以能用BFS,就用BFS。

我们将起点放入队列,然后取出点后,判断这个点可以走到哪些点,满足条件的点,才进队列。因此,最后其实就是求,总共有多少个点进过队列。

因此,对于BFS而言,每个点只考虑一次,因此时间复杂度是 O(N * M) 不会超市

 

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 1e3 + 15;

int vis[MAXN][MAXN];
int dx[4] = {-1, 1, 0, 0}, dy[4] = {0, 0, -1, 1};

int main()
{
    int n, m, x, y;
    cin >> n >> m >> x >> y;
    vector<string> grid;

    for(int i = 0;i < n;++i)  // 一开始的图
    {
        string tep;
        cin >> tep;
        grid.push_back(tep);
    }

    queue<pair<int, int> > q;  // 队列放的 x 和 y一个左边

    while(!q.empty()) q.pop();

    memset(vis, 0, sizeof(vis));

    int res = 0;
    if(grid[x][y] == '1')  // 从起点开始
    {
        q.push(make_pair(x, y));
        vis[x][y] = 1;
        ++res;
    }
    
    while(!q.empty())
    {
        int cx = q.front().first, cy = q.front().second;
        q.pop();

        for(int i = 0;i < 4;++i)  // 从这个点开始往四个方向走,当满足条件,才进队列
        {
            int tx = cx + dx[i], ty = cy + dy[i];
            if(tx < 0 || tx >= n || ty < 0 || ty >= m) continue;
            if(grid[tx][ty] == '0') continue;
            if(vis[tx][ty] == 1) continue;
            vis[tx][ty] = 1;
            ++res;
            q.push(make_pair(tx, ty));
        }
    }
    cout << res << endl;
    return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
蓝桥杯是中国著名的计算机竞之一,包括蓝桥杯全国软件和信息技术专业人才大以及蓝桥杯全国青少年程序设计比赛。其中,蓝桥杯python选拔赛是为了选拔具有优秀python编程能力的参者而举办的事。 蓝桥杯python选拔赛注重考察参者对python语言的理解和应用能力。在比赛中的题目往往涵盖了python编程的各个方面,包括基本语法、数据结构、算法设计和程序设计思维等。选手需要在规定的时间内,按照题目要求,使用python语言编写解决方案。 参加蓝桥杯python选拔赛可以提高参者的编程能力和解决问题的能力。通过参加比赛,选手们可以接触到各种不同类型的编程题目,锻炼自己的编程思维和动手能力。同时,比赛还提供了一个交流和学习的平台,选手们可以与其他参者分享经验、学习别人的优秀解法,提高自己的编程水平。 此外,蓝桥杯python选拔赛还是选拔参加蓝桥杯全国软件和信息技术专业人才大的入场券。优秀的选手将有机会代表自己的学校或单位参,并在比赛中展示自己的才华。 总之,蓝桥杯python选拔赛是一个提高编程水平、展示自己才能的良好平台。参加比赛不仅可以锻炼自己的编程能力,还可以结识其他编程爱好者,促进学习和交流。相信通过努力和参与,每个选手都能在蓝桥杯python选拔赛中获得进步和收获。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值