2025年第八届传智杯初赛第二场题解

第八届传智杯初赛第二场题解

题目链接:Link(2025年第八届传智杯初赛第二场)

第一题:

【回忆题目】 已知今天是个星期 a a a 1 ≤ a ≤ 7 1 \le a \le 7 1a7),请问经过 b b b b ≥ 0 b \ge 0 b0)天后,是星期几?

结果 = ( 当前星期几 + 经过天数 − 1 )   m o d   7 + 1 \text{结果} = (\text{当前星期几} + \text{经过天数} - 1) \bmod 7 + 1 结果=(当前星期几+经过天数1)mod7+1

AC代码:c语言

#include <stdio.h>

int main() {
    long long a, b; 
    scanf("%lld %lld", &a, &b);
    printf("%lld\n", (a + b - 1) % 7 + 1);
    return 0;
}

第二题:

【回忆题目】 你有 n n n 个水果,每个水果都有两个属性:酸度和甜度。现在你需要从中挑选出 k k k 个水果。挑选的规则如下:使得选出的 k k k 个水果的甜度总和达到最大。在满足甜度总和最大的前提下,使得这 k k k 个水果的酸度总和尽可能小。

请输出最终挑选出的 k k k 个水果的甜度总和及酸度总和。

AC代码:c语言

解法一:自定义排序

如果甜度不同,按甜度降序排列 (从大到小)

如果甜度相同,按酸度升序排列 (从小到大)

#include <stdio.h>
#include <stdlib.h>

typedef struct {
    int acid;
    int sweet;
} Fruit;

Fruit fruits[200005];

// 排序逻辑
int cmp(const void *a, const void *b) {
    Fruit *f1 = (Fruit *)a;
    Fruit *f2 = (Fruit *)b;
    
    // 甜度升序
    if (f1->sweet != f2->sweet) {
        return f1->sweet - f2->sweet;
    }
    // 甜度相同时,酸度降序
    return f2->acid - f1->acid;
}

int main() {
    int n, k;
    scanf("%d %d", &n, &k);
    
    for (int i = 0; i < n; i++) {
        scanf("%d", &fruits[i].acid);
    }
    for (int i = 0; i < n; i++) {
        scanf("%d", &fruits[i].sweet);
    }

    qsort(fruits, n, sizeof(Fruit), cmp);

    long long sum_sweet = 0, sum_acid = 0;

    for (int i = 0; i < k; i++) {
        sum_sweet += fruits[i].sweet;
        sum_acid += fruits[i].acid;
    }

    printf("%lld %lld\n", sum_sweet, sum_acid);

    return 0;
}
解法二:排序+滑动窗口

最简单解法为解法一,解法二可自行了解

第三题:

【回忆题目】 给定 T T T 组测试数据,每组包含一个字符串。对于字符串中的第 i i i 个字符(下标从 1 开始),如果 i i i 的二进制表示中包含奇数个 1,则将该字符转换为大写字母;否则保持不变。字符串长度 1 < = l e n < = 500 1<=len<=500 1<=len<=500

AC代码:c语言

解法一:预处理+模拟

本题 T T T 组样例容易超时,所以预处理空间换时间

#include <stdio.h>
#include <string.h>

int needs_upper[505];

// 常规方案
int count_ones(int n) {
    int c = 0;
    while (n > 0) {
        c += (n & 1); // 如果最低位是 1,计数加 1
        n >>= 1;      // 右移一位,检查下一位
    }
    return c;
}

// 优化方案:Brian Kernighan 算法,最好记住
// int count_ones(int n) {
//     int count = 0;
//     while (n > 0) {
//         n &= (n - 1); 
//         count++;
//     }
//     return count;
// }

void init() {
    for (int i = 1; i <= 500; i++) {
        if (count_ones(i) % 2 != 0) {
            needs_upper[i] = 1; // 奇数个 1,标记为需要转换
        } else {
            needs_upper[i] = 0;
        }
    }
}

int main() {
    init();

    int t;
    scanf("%d", &t);

    char s[505];
    while (t--) {
        scanf("%s", s);
        
        int len = strlen(s);
        for (int i = 1; i <= len; i++) {
            // 直接查表,时间复杂度 O(1)
            if (needs_upper[i]) {
                if (s[i-1] >= 'a' && s[i-1] <= 'z') {
                    s[i-1] -= 32;
                }
                // 直接用库函数
                // s[i-1] = toupper(s[i-1]);
            }
        }
        printf("%s\n", s);
    }
    return 0;
}

第四题:

【回忆题目】 给定一个长度为 n n n 的字符串,如果将其看作一个环(最后一个字符和第一个字符相邻),求出由相同字符组成的最长子串的长度。

AC代码:c语言

解法一:双指针
#include <stdio.h>
#include <string.h>

int main() {
    char s[1000005];
    int n, l = 0, r, cnt, ans = 0;

    scanf("%s", s);
    n = strlen(s);

    while (l < n) {
        r = l;    // 右指针从左指针位置开始
        cnt = 0;  // 计数当前段长度

        // r 指针通过取模 % n 向后探索
        // 限制 cnt < n 防止全同字符导致死循环
        while (cnt < n && s[r % n] == s[l]) {
            r++;   // r 持续向右移动
            cnt++; // 计数增加
        }

        // 更新最大长度
        if (cnt > ans) ans = cnt;

        // 如果已经达到最大可能长度(全串相同),直接退出
        if (ans == n) break;

        // 左指针跳跃到右指针当前指向的不同字符位置
        // 注意:由于是取模逻辑,l 直接增加 cnt 即可
        l += cnt;
    }

    printf("%d\n", ans);
    return 0;
}

第五题:

【回忆题目】 给定一个含有 n n n 个正整数的数组 A = [ a 1 , a 2 , … , a n ] A = [a_1, a_2, \dots, a_n] A=[a1,a2,,an]。你可以选择一个非负整数 x x x,并将数组中的每个元素都加上 x x x,得到新数组 A ′ = [ a 1 + x , a 2 + x , … , a n + x ] A' = [a_1+x, a_2+x, \dots, a_n+x] A=[a1+x,a2+x,,an+x]。 你的目标是找到一个 x x x,使得新数组所有元素的最大公约数 (GCD) 达到最大。如果存在多个这样的 x x x,请求出最小的那个。

思路:

直接通过循环累加 g g g 在数学逻辑上是行不通的,因为当数组中的数很大时,单纯的模拟会导致超时。我们需要利用 GCD 的一个关键性质:

性质: gcd ⁡ ( x , y ) = gcd ⁡ ( x , y − x ) \gcd(x, y) = \gcd(x, y - x) gcd(x,y)=gcd(x,yx)

推广到多个数:
gcd ⁡ ( a 1 , a 2 , a 3 , … , a n ) = gcd ⁡ ( a 1 , a 2 − a 1 , a 3 − a 2 , … , a n − a n − 1 ) \gcd(a_1, a_2, a_3, \dots, a_n) = \gcd(a_1, a_2-a_1, a_3-a_2, \dots, a_n-a_{n-1}) gcd(a1,a2,a3,,an)=gcd(a1,a2a1,a3a2,,anan1)
当我们给所有数都加上 x x x 后,数组变为 a i + x a_i + x ai+x。此时它们的 GCD 为:
G = gcd ⁡ ( a 1 + x , ( a 2 + x ) − ( a 1 + x ) , ( a 3 + x ) − ( a 2 + x ) , …   ) G = \gcd(a_1+x, (a_2+x)-(a_1+x), (a_3+x)-(a_2+x), \dots) G=gcd(a1+x,(a2+x)(a1+x),(a3+x)(a2+x),)
简化后:
G = gcd ⁡ ( a 1 + x , a 2 − a 1 , a 3 − a 2 , … , a n − a n − 1 ) : G = \gcd(a_1+x, a_2-a_1, a_3-a_2, \dots, a_n-a_{n-1}): G=gcd(a1+x,a2a1,a3a2,,anan1)

除了第一项 a 1 + x a_1+x a1+x 以外,后面所有的差值(如 a 2 − a 1 a_2-a_1 a2a1)都与 x x x 无关。这意味着无论你加多少, G G G 永远是这些差值的公约数。

为了让 G G G 最大,最好的情况就是让 G G G 等于所有差值的最大公约数。令
g d i f f = gcd ⁡ ( ∣ a 2 − a 1 ∣ , ∣ a 3 − a 2 ∣ , … , ∣ a n − a n − 1 ∣ ) g_{diff} = \gcd(|a_2-a_1|, |a_3-a_2|, \dots, |a_n-a_{n-1}|) gdiff=gcd(a2a1,a3a2,,anan1)
我们要找到最小的非负整数 x x x,使得第一个元素 a 1 + x a_1 + x a1+x 能被 g d i f f g_{diff} gdiff 整除。

利用取模运算: a 1 ( m o d g d i f f ) a_1 \pmod{g_{diff}} a1(modgdiff),如果余数为 0, x = 0 x = 0 x=0,如果余数不为 0, x = g d i f f − ( a 1 ( m o d g d i f f ) ) x = g_{diff} - (a_1 \pmod{g_{diff}}) x=gdiff(a1(modgdiff))

AC代码:c语言
#include <stdio.h>

long long gcd(long long a, long long b) {
    return b == 0 ? a : gcd(b, a % b);
}

int main() {
    int n;
    long long first, current, g = 0;

    scanf("%d %lld", &n, &first);

    // 循环处理后续数字,节省内存
    for (int i = 1; i < n; i++) {
        scanf("%lld", &current);
        // 计算差值的绝对值
        long long diff = (current > first) ? (current - first) : (first - current);
        g = gcd(g, diff);
    }

    // 计算最小加数 x,使得 (first + x) % g == 0
    long long x = (g - (first % g)) % g;
    printf("%lld %lld\n", g, x);
    
    return 0;
}

第六题:

【回忆题目】 给定一个整数 n n n,代表楼梯的总阶数。你从第 0 阶出发,向上爬到第 n n n 阶。左脚只能踩在奇数阶,右脚只能踩在偶数阶;必须左右脚交替行进(即不能连续用同一只脚迈步);第一步可以迈左脚或右脚。

计算到达第 n n n 阶的所有可能走法的总数,结果对 1 0 9 + 7 10^9 + 7 109+7 取模。

解法一:动态规划
解法二:矩阵快速幂

先列出解法,后续补充思路和代码

为了在2022第五届初赛中取得优异成绩,高效地复习算法数据结构知识至关重要。推荐使用《2022第五届初赛题解析与技术要点》这一资源,因为它详细解析了比赛中的关键题目和解题思路,有助于你快速定位自己的薄弱环节并加以强化。复习时,请按照以下步骤进行: 参考资源链接:[2022第五届初赛题解析与技术要点](https://wenku.youkuaiyun.com/doc/7h56xub0jg?spm=1055.2569.3001.10343) 1. 理解题目要求:首先仔细阅读赛题文档,理解每个题目的具体要求和限制条件,确保对问题有清晰的认识。 2. 分类复习:将赛题按照算法数据结构的类型进行分类,比如排序算法、图论算法、动态规划等,这样可以帮助你系统地复习。 3. 深入学习:对于每一类算法数据结构,深入研究其原理、应用场景和常见问题。例如,动态规划问题通常需要分析最优子结构、边界条件和状态转移方程。 4. 实践编程:实际编写代码来解决赛题,这不仅加深理解,还能提高编码能力。建议在编程时多尝试不同的算法数据结构,找到最优解。 5. 错题回顾:遇到问题或错误时,要及时回顾相关知识点,并查看《2022第五届初赛题解析与技术要点》中的解析,从中学习解题技巧和优化思路。 6. 时间管理:练习时注意时间管理,尽量在规定时间内完成题目,模拟考试场景,以适应竞赛的时间压力。 通过上述步骤,结合《2022第五届初赛题解析与技术要点》这本资料,相信你能更有效地复习,为比赛做好充分准备。祝你在初赛中取得好成绩! 参考资源链接:[2022第五届初赛题解析与技术要点](https://wenku.youkuaiyun.com/doc/7h56xub0jg?spm=1055.2569.3001.10343)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值