学而思编程每周练习赛 | 2026年春第3周

语言基础组

元素分类

【题目来源】

学而思编程:元素分类

【题目描述】

给定 \(n\) 个整数元素 \(x_i\)。现在,需要你对这 \(n\) 个整数元素进行分类。

每个元素要么划分至 \(b\) 类,要么划分至 \(c\) 类。

我们设所有 \(b\) 类元素的相加之和为 \(sum_b\),所有 \(c\) 类元素的相加之和为 \(sum_c\)

请你计算 \(sum_b−sum_c\) 的最大可能值。

如果某类元素的个数为 \(0\),则该类元素的相加之和视为 \(0\)

【输入】

第一行包含整数 \(n\)

第二行包含 \(n\) 个整数 \(x_i\)

【输出】

输出一个整数,表示 \(sum_b−sum_c\) 的最大可能值。

【输入样例】

3
1 -2 0

【输出样例】

3

【代码详解】

#include <bits/stdc++.h>
using namespace std;
int n, sum1, sum2;  // sum1: 正数和, sum2: 负数和

int main()
{
    cin >> n;
    for (int i = 1; i <= n; i++)
    {
        int num;
        cin >> num;
        // 根据正负分别累加
        if (num >= 0)
        {
            sum1 += num;  // 累加正数
        }
        else
        {
            sum2 += num;  // 累加负数
        }
    }
    // 输出正数和减去负数和
    cout << sum1 - sum2 << endl;
    return 0;
}

【运行结果】

3
1 -2 0
3

体育课

【题目来源】

学而思编程:体育课

【题目描述】

小猴是学校的体育器材的管理员,现在因为器材老化需要购买一批新的体育器材,具体每一种体育器材需要购买多少取决于当前学期每一种器材的借用情况。

小猴发现这学期的体育选修课只有三类:羽毛球、乒乓球和篮球。现在小猴请你根据这学期的体育课情况来统计一下这学期一共借出了多少器材(只包含羽毛球、乒乓球和篮球),每种器材分别借出的多少次,每种器材借出次数占所有器材借出总次数的百分比分别是多少。

【输入】

第一行一个整数 \(n\),表示这学期体育课的次数。

接下来 \(n\) 行,每行两个整数 \(a_i\) (表示第 \(i\) 节体育课借用器材的次数)和 \(b_i\)(表示第 \(i\) 节体育课借用的器材种类,\(0\) 表示羽毛球,\(1\) 表示乒乓球,\(2\) 表示篮球)。

【输出】

一共有 \(3\) 行:

第一行一个整数表示一共借出了多少器材;

第二行三个空格隔开整数分别表示羽毛球、乒乓球和篮球这学期借出的次数;

第三行三个空格隔开百分数表示羽毛球、乒乓球和篮球借出次数占所有器材总借出次数的占比值,占比值保留两位小数。

【输入样例】

10
10 0
6 1
15 2
5 0
14 1
9 0
6 1
8 2
5 0
14 1

【输出样例】

92
29 40 23
31.52% 43.48% 25.00%

【代码详解】

#include <bits/stdc++.h>
using namespace std;
int n, cnt[5];  // cnt数组用于统计不同类型(0,1,2)的数量

int main()
{
    cin >> n;
    int sum = 0;  // 记录总数
    for (int i = 1; i <= n; i++)
    {
        int a, b;
        cin >> a >> b;  // a: 数量, b: 类型
        cnt[b] += a;    // 累加到对应类型
        sum += a;       // 累加到总数
    }
    // 输出总数
    cout << sum << endl;
    // 输出各类别的数量
    for (int i = 0; i <= 2; i++)
    {
        cout << cnt[i] << " ";
    }
    cout << endl;
    // 输出各类别占总数的百分比
    for (int i = 0; i <= 2; i++)
    {
        printf("%.2f%% ", cnt[i] * 100.00 / sum);
    }
    return 0;
}

【运行结果】

10
10 0
6 1
15 2
5 0
14 1
9 0
6 1
8 2
5 0
14 1
92
29 40 23
31.52% 43.48% 25.00%

公平分配

【题目来源】

学而思编程:公平分配

【题目描述】

小猴和小美从猴博士那里得到 \(n\) 个糖果。每一颗糖果重 \(1\) 克或 \(2\) 克。现在他们想要公平地将糖果分成两组,使得两组糖果的重量(每一组糖果的重量之和)相等。他们想要你帮助判断糖果是否能恰好分成两组。

【输入】

输入第一行一个整数 \(t\),表示测试数据的组数。

接下来,每组数据第一行一个整数 \(n\) 表示两人收到的糖果数量。

接下来一行 \(n\) 个整数 \(w_1,w_2,\dots,w_n\),表示每颗糖果的重量。

【输出】

输出共 \(t\) 行。对于每组测试数据,如果可以分成重量相等的两组,则输出 YES,否则输出 NO。

【输入样例】

4
1
2
2
1 2
3
1 1 1
4
2 2 2 2

【输出样例】

NO
NO
NO
YES

【代码详解】

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

int main()
{
    cin >> n;
    for (int i = 1; i <= n; i++)
    {
        int num;
        cin >> num;  // 读取数字个数
        int cnt = 0;  // 统计值为1的个数
        for (int j = 1; j <= num; j++)
        {
            int w;
            cin >> w;
            if (w == 1)
            {
                cnt++;  // 统计1的个数
            }
        }
        // 判断条件1:如果1的个数是偶数且不为0
        if (cnt % 2 == 0 && cnt != 0)
        {
            cout << "YES" << endl;
        }
        // 判断条件2:如果没有1,但非1的数字个数是偶数
        else if ((num - cnt) % 2 == 0 && cnt == 0)
        {
            cout << "YES" << endl;
        }
        else
        {
            cout << "NO" << endl;
        }
    }
    return 0;
}

【运行结果】

4
1
2
NO
2
1 2
NO
3
1 1 1
NO
4
2 2 2 2
YES

普及奠基组

斐波那契字符串

【题目来源】

学而思编程:斐波那契字符串

【题目描述】

斐波那契数列指的是这样一个数列:\(1\)\(1\)\(2\)\(3\)\(5\)\(8\)\(13\)\(21\)\(34\)、......

在数学上,斐波那契数列以如下递推的形式定义:\(F(0)=1\)\(F(1)=1\),\(F(n)=F(n−1)+F(n−2)(n≥2)\)

如果一个数出现在斐波那契数列之中,那么我们就称这个数为斐波那契数。

现在,给定一个整数 \(n\),请你构造一个长度为 \(n\) 的字符串 \(s_1s_2\dots s_n\)

对于字符串中的第 \(i\) 个字符 \(s_i\)

  • 如果 \(i\) 是斐波那契数,则 \(s_i\) 为大写字母 O。
  • 如果 \(i\) 不是斐波那契数,则 \(s_i\) 为小写字母 o。

输出构造好的字符串。

注意,字符下标从 \(1\)\(n\)

【输入】

一行一个整数 \(n\)

【输出】

一个字符串,表示答案。

【输入样例】

3

【输出样例】

OOO

【代码详解】

#include <bits/stdc++.h>
using namespace std;
int n, f[50];  // f: 存储斐波那契数列
bool flag[100005];  // flag: 标记是否是斐波那契数

int main()
{
    cin >> n;
    f[0] = 1, f[1] = 1;  // 初始化斐波那契数列
    flag[1] = true;  // 1是斐波那契数
    
    // 生成不超过n的斐波那契数列
    for (int i = 2; f[i - 1] <= n; i++)
    {
        f[i] = f[i - 1] + f[i - 2];
        flag[f[i]] = true;  // 标记斐波那契数
    }
    
    // 输出序列:斐波那契数输出"O",否则输出"o"
    for (int i = 1; i <= n; i++)
    {
        if (flag[i] == true)
        {
            cout << "O";
        }
        else
        {
            cout << "o";
        }
    }
    return 0;
}

【运行结果】

3
OOO

跑车与旅行

【题目来源】

学而思编程:跑车与旅行

【题目描述】

小猴终于打算驾驶自己心爱的小跑车去旅行了,为此他已经规划好了一个旅游线路,要依次参观 \(n\) 个城市(包括自己的城市)。小猴有加油强迫症,即每到一个城市,一定会将自己跑车的油箱彻底加满,否则就会浑身难受。

已知小猴的跑车每跑一公里都会消耗一升汽油,现在依次告诉你每个城市中每升汽油的价格,以及该城市与上一个到达城市的距离(单位为公里,起点城市距离自己为 \(0\))。请你帮助小猴计算到达终点城市的总加油花费(当然到最后一个城市也需要加油),但是如果小猴到不了最后的城市,请提前告诉他。

【输入】

第一行两个正整数 \(n,c\),分别表示城市的数量与小猴跑车的油箱容量。

第二行 \(n\) 个整数,表示该城市与上一个城市的距离 \(d_i\)

第三行 \(n\) 个整数,表示该城市每升汽油的价格 \(p_i\)

【输出】

如果小猴能够到达,请输出购买汽油总花费,否则输出他最后一个到达城市的编号。

【输入样例】

3 3
0 2 3
3 2 1

【输出样例】

16

【代码详解】

#include <bits/stdc++.h>
using namespace std;
int n, c, d[100005], p[100005];

// 计算最小消耗
long long cal()
{
    long long sum = c * p[1];  // 初始化总和为第一个位置的成本
    for (int i = 2; i <= n; i++)
    {
        // 如果当前位置距离大于c,则无法到达
        if (d[i] > c)
        {
            return i - 1;  // 返回上一个位置
        }
        else
        {
            sum += d[i] * p[i];  // 累加消耗
        }
    }
    return sum;  // 返回总消耗
}

int main()
{
    cin >> n >> c;
    for (int i = 1; i <= n; i++)
    {
        cin >> d[i];  // 读取距离
    }
    for (int i = 1; i <= n; i++)
    {
        cin >> p[i];  // 读取单位消耗
    }
    cout << cal() << endl;
    return 0;
}

【运行结果】

3 3
0 2 3
3 2 1
16

阶梯求和

【题目来源】

学而思编程:阶梯求和

【题目描述】

猴博士定义了一个"阶梯和"的概念------简单来说,对于一个数组 \(a_1,...,a_n\),它的数组阶梯和就是:\(1 \times a_1,...,n \times a_n\)

现在给你一个数组 \(a_1,...,a_n\),然后猴博士会给你 \(m\) 个区间 \([L_i,R_i]\),请你依次求出其区间内数组的"阶梯和"。

例如,对于数组 \(1,2,3,4\)

  • 区间 \([2,4]\) 的"阶梯和"为:\(1 \times 2 + 2 \times 3 + 3 \times 4 = 20\)
  • 区间 \([1,2]\) 的"阶梯和"为:\(1 \times 1 + 2 \times 2 = 5\)

【输入】

第一行两个正整数 \(n,m\)

第二行 \(n\) 个整数 \(a_1,...,a_n\)

接着 \(m\) 行,每行两个正整数 \(L_i,R_i\),表示需要求解的区间。

【输出】

\(m\) 行,每行一个整数表示答案。

【输入样例】

4 2
1 2 3 4
2 4
1 2

【输出样例】

20
5

【代码详解】

#include <bits/stdc++.h>
using namespace std;
int n, m;
long long sum1[1000005], sum2[1000005];  // sum1: 前缀和, sum2: 加权前缀和

int main()
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    cin >> n >> m;
    for (int i = 1; i <= n; i++)
    {
        int x;
        cin >> x;
        sum1[i] = sum1[i - 1] + x;  // 计算普通前缀和
        sum2[i] = sum2[i - 1] + i * x;  // 计算加权前缀和:索引 * 值
    }
    while (m--)
    {
        int l, r;
        cin >> l >> r;
        // 计算公式:sum2[r]-sum2[l-1] - (l-1)*(sum1[r]-sum1[l-1])
        long long ans = sum2[r] - sum2[l - 1] - (l - 1) * (sum1[r] - sum1[l - 1]);
        cout << ans << "\n";
    }
    return 0;
}

【运行结果】

4 2
1 2 3 4
2 4
1 2
20
5

普及进阶组

买整数

【题目来源】

学而思编程:买整数

【题目描述】

小猴去一个整数商店买一个整数。

整数商店出售范围在 \(1 \sim 10^9\) 的所有整数,整数 \(n\) 的售价是 \(a \times n+b \times d(n)\) 元,其中 \(d(n)\)\(n\) 的十进制表示的位数。

例如 \(a=10,b=7\) 时,以下 \(3\) 个整数的价格是:

\(10\)\(10 \times 10 + 7 \times 2 = 114\)

\(100\)\(10 \times 100 + 7 \times 3 = 102\)

\(12345\)\(10 \times 12345 + 7 \times 5 = 123485\)

小猴只带了 \(x\) 元钱,求出他能买到的最大整数,如果一个整数都买不起,输出 \(0\)

【输入】

\(1\) 行,\(3\) 个正整数 \(a,b,x\)

【输出】

\(1\) 个整数,输出小猴能买到的最大整数,如果一个整数都买不起,输出 \(0\)

【输入样例】

10 7 100

【输出样例】

9

【解题思路】

image

【代码详解】

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

int a, b, k;

// 检查数字x是否满足条件
bool check(int x)
{
    int t = x;  // 保存原始数字
    int len = 0;  // 计算数字x的位数
    while (x)
    {
        len++;
        x /= 10;
    }

    // 检查 a*x + b*位数 是否不超过k
    return (a * t + b * len) <= k;
}

signed main()
{
    cin >> a >> b >> k;
    int l = 0, r = 1e9;  // 二分查找范围
    while (l < r)
    {
        int mid = (l + r + 1) / 2;
        if (check(mid))
        {
            l = mid;  // 满足条件,尝试更大的数
        }
        else
        {
            r = mid - 1;  // 不满足条件,减小范围
        }
    }
    cout << l << endl;
    return 0;
}

【运行结果】

10 7 100
9

数列分段3

【题目来源】

学而思编程:数列分段3

【题目描述】

给出n项的数列,要求将数列划分成若干段,且每一段的平均数都不超过k。 求最多能划分成多少段?

【输入】

第1行,两个正整数n,k

第2行,n个正整数\(a_1,a_2,\cdots,a_n\)

【输出】

一个整数,最多能划分成多少段。

如果不论如何划分也不能让每段平均数不超过k,输出-1。

【输入样例】

7 7
9 9 4 3 9 7 4 

【输出样例】

3

【解题思路】

image

【代码详解】

#include <bits/stdc++.h>
using namespace std;
const int N = 5005;

int n, k;
int a[N], sa[N];  // a: 原数组, sa: 前缀和数组
int dp[N];  // dp[i]: 前i个元素最多能分成多少段

int main()
{
    cin >> n >> k;
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];
        sa[i] = sa[i - 1] + a[i];  // 计算前缀和
    }
    // 初始化dp数组为负无穷
    memset(dp, -0x3f, sizeof(dp));
    dp[0] = 0;  // 空序列有0段

    for (int i = 1; i <= n; i++)
    {
        for (int j = 0; j < i; j++)
        {
            // 检查区间[j+1, i]是否满足条件
            if ((sa[i] - sa[j]) <= k * (i - j))
            {
                // 如果可以分割,更新dp值
                dp[i] = max(dp[i], dp[j] + 1);
            }
        }
    }
    // 输出结果
    if (dp[n] < 0)
    {
        cout << -1 << endl;  // 无法分割
    }
    else
    {
        cout << dp[n] << endl;  // 输出最大段数
    }
    return 0;
}

【运行结果】

7 7
9 9 4 3 9 7 4 
3
posted @ 2026-03-23 14:43  团爸讲算法  阅读(4)  评论(0)    收藏  举报