6 一维数组综合应用

正在加载视频播放器…

GESP C++ 三级:一维数组综合应用

核心目标:在掌握一维数组基本操作的基础上,综合运用数组与模拟法、枚举法、统计法,解决多逻辑组合的编程问题,熟悉GESP三级真题的命题趋势与解题套路。
数组是“数据容器”,模拟法是“忠实复现规则”,枚举法是“逐一验证可能”;综合题的难点不在语法,而在读懂规则、理清步骤、守住边界。

一、题型一:数组 + 模拟法

1. 核心方法论

模拟法的本质是:把题目描述的规则,用代码一步一步忠实复现出来。不需要数学推导,不需要找公式,只需要“照猫画虎”。

模拟法解题三步框架:

第一步:识别状态变量(哪些量在变化)
第二步:确定每一步的操作规则(按顺序执行什么)
第三步:确定终止条件(什么时候停止)

2. 真题精讲:数组清零

题目描述:

小A有一个由n个非负整数构成的数组。重复进行以下操作,直到数组只包含0:

  1. 找到最大的整数,记其下标为k。如果有多个最大值,选择下标最大的。
  2. 从所有不为零的整数中找到最小的整数。
  3. 将第一步找到的数减去第二步找到的数。

求需要多少次操作才能使得数组全部变成0。

示例:[2, 3, 4] 需要 7 次操作变成 [0, 0, 0]。

解题思路:

这道题是典型的模拟法——规则非常明确,只需要把三个步骤翻译成代码,循环执行直到数组全为0。

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

int main() {
    int n;
    cin >> n;
    int a[105];
    for (int i = 0; i < n; i++) cin >> a[i];

    int ops = 0;
    while (true) {
        // 检查是否全部为0
        bool allZero = true;
        for (int i = 0; i < n; i++) {
            if (a[i] != 0) { allZero = false; break; }
        }
        if (allZero) break;

        // 步骤1:找最大元素,多个取下标最大的
        int maxIdx = -1;
        for (int i = 0; i < n; i++) {
            if (a[i] > 0) {
                if (maxIdx == -1 || a[i] >= a[maxIdx]) {
                    maxIdx = i;   // 注意用 >= 取下标最大的
                }
            }
        }

        // 步骤2:找最小非零元素
        int minVal = 1000000000;
        for (int i = 0; i < n; i++) {
            if (a[i] > 0 && a[i] < minVal) {
                minVal = a[i];
            }
        }

        // 步骤3:最大值减去最小值
        a[maxIdx] -= minVal;
        ops++;
    }

    cout << ops << endl;
    return 0;
}

关键细节:

细节 说明
a[i] >= a[maxIdx] 用 >= 而非 >,保证多个最大值时取下标最大的
a[i] > 0 条件 只在非零元素中找最大/最小
allZero 检查 每次循环开始前检查,全0则退出
数据规模 n≤100,aᵢ≤100,模拟次数有限,不会超时

3. 模拟法的通用模板

// 1. 读入初始状态
// 2. while (未达到终止条件) {
//        a. 检查终止条件
//        b. 按题目规则,逐步更新状态
//        c. 记录操作次数/输出中间结果
//    }
// 3. 输出最终结果

边界自测规则:每写完一道模拟题,必须自行设计3组边界数据:

  • n = 1(只有一个元素)
  • 全0数组(一开始就满足终止条件)
  • 全最大值(模拟次数最多的情况)

二、题型二:数组 + 枚举/统计法

1. 核心方法论

枚举法的本质是:遍历所有可能的情况,用条件筛选出正确答案。数组在这里充当“数据容器”和“结果记录器”。

枚举法解题框架:

外层循环:遍历所有可能的“候选”
内层逻辑:对每个候选,判断是否满足条件
结果处理:计数、累加、更新最值、标记

2. 真题精讲:小杨的智慧购物

题目描述:

小杨需要购买M种文具。商店里共有N件文具,每件有种类K和价格P。对于每种文具,只购买最便宜的那一件。计算买齐所有M种文具的最小总花费。

解题思路:

核心是维护一个“每种文具最低价格”的数组,遍历所有商品时不断更新,最后求和。

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

const int INF = 1000000000;

int main() {
    int m, n;
    cin >> m >> n;

    int minPrice[1005];
    for (int i = 1; i <= m; i++) minPrice[i] = INF;

    for (int i = 0; i < n; i++) {
        int k, p;
        cin >> k >> p;
        if (p < minPrice[k]) {
            minPrice[k] = p;
        }
    }

    int total = 0;
    for (int i = 1; i <= m; i++) {
        total += minPrice[i];
    }
    cout << total << endl;
    return 0;
}

关键细节:

细节 说明
数组大小 1005,留足余量,防止种类编号越界
初始值设为INF 保证第一次遇到真实价格时一定能更新
下标从1开始 种类编号是1~M,数组下标与之对应
时间复杂度 O(N),只需遍历一遍商品

3. 统计法:桶数组的应用

当元素范围有限且连续时,可以用数组作“桶”统计频次。

直方图统计(GESP三级常考):

// 统计数组中每个数出现的次数,只统计到最大值m
const int M = 1005;
int bucket[M] = {0};
int maxVal = 0;

for (int i = 0; i < n; i++) {
    bucket[arr[i]]++;
    if (arr[i] > maxVal) maxVal = arr[i];
}

for (int v = 0; v <= maxVal; v++) {
    cout << bucket[v] << endl;   // 每个数出现的次数
}

桶数组的适用条件:

  • 元素范围已知且有限(如0~1000)。
  • 需要频繁查询某个值出现的次数。
  • 不需要对元素本身做复杂运算。

三、题型三:数组 + 多条件组合

1. 核心方法论

三级综合题的最新趋势是“多条件组合”——不再是单一操作,而是要求在一个循环中完成多个统计目标,或者在多个条件之间做判断和筛选。

多条件组合题的解题框架:

1. 列出所有需要统计/判断的条件
2. 确定哪些条件可以在同一次遍历中完成
3. 设计状态变量(计数器、最值变量、标记数组)
4. 一次遍历中同步更新所有状态
5. 输出所有统计结果

2. 典型模型:一次遍历完成多项统计

题目:输入n个数,统计其中正数个数、负数个数、零的个数,并求所有数的绝对值之和。

int positive = 0, negative = 0, zero = 0;
long long absSum = 0;

for (int i = 0; i < n; i++) {
    int x;
    cin >> x;
    if (x > 0) positive++;
    else if (x < 0) negative++;
    else zero++;

    if (x >= 0) absSum += x;
    else absSum += -x;
}

cout << positive << " " << negative << " " << zero << " " << absSum << endl;

关键:所有统计变量必须在循环外初始化,循环内只更新。

3. 典型模型:多条件筛选

题目:从数组中找出同时满足以下条件的元素:大于平均值、是偶数、能被3整除。输出这些元素。

// 第一步:求平均值
int sum = 0;
for (int i = 0; i < n; i++) sum += arr[i];
double avg = 1.0 * sum / n;

// 第二步:多条件筛选
for (int i = 0; i < n; i++) {
    if (arr[i] > avg && arr[i] % 2 == 0 && arr[i] % 3 == 0) {
        cout << arr[i] << " ";
    }
}

4. 典型模型:标记数组的应用

标记数组用于记录“某个位置/某个值是否被访问过”或“是否满足条件”。

题目:找出数组中所有重复出现的元素(只输出一次)。

const int M = 1005;
bool appeared[M] = {false};   // 标记是否已经出现过
bool printed[M] = {false};    // 标记是否已经输出过

for (int i = 0; i < n; i++) {
    if (appeared[arr[i]] && !printed[arr[i]]) {
        cout << arr[i] << " ";
        printed[arr[i]] = true;
    }
    appeared[arr[i]] = true;
}

四、易错点与边界处理

1. 综合应用题的高频失分点

失分点 原因 正确做法
下标写反 arr[i] 写成 arr[j] 遍历时先在草稿纸上画出前3个元素的下标
循环边界错误 i < n 写成 i <= n 三级数组题统一用 i < n,除非题目明确要求1~n
累加变量在循环内初始化 int sum = 0; 写在循环体内 所有统计变量必须在循环外初始化
最值初值取0 数组全为负数时结果错误 最值初值取 arr[0] 或一个足够大/小的数
忘记特判n=0 空数组时循环不执行,但逻辑可能出错 读入后先检查 if (n == 0)
数组大小不够 n 可能达到1000,数组只开了100 数组大小留足余量,通常开 n + 5
模拟题漏步骤 题目描述三步,代码只写了两步 把题目步骤逐条列在注释里,逐条对照

2. 三级编程题的“3组边界自测”规则

每写完一道数组综合题,必须自行设计3组边界测试数据:

第1组:n = 1(最小规模,验证基本逻辑)
第2组:全0 / 全相同值(特殊输入,验证终止条件)
第3组:全最大值(验证是否会溢出、是否会超时)

3. 数组遍历的“画图对照法”

数组遍历类题目,写完后先在草稿纸上画出前3个元素的运行结果,和代码输出对照,避免下标写反。这类错误在近3场考试里占编程题丢分的40%以上。

五、速查表

需求 核心写法 关键细节
模拟法 while (未终止) { 按规则更新; ops++; } 步骤不能少、顺序不能乱
枚举法 for (所有可能) { if (条件) 处理; } 条件要覆盖全部情况
统计法 bucket[值]++; 桶大小要足够
最值更新 if (x < minv) minv = x; 初值取INF或arr[0]
多条件统计 循环外初始化,循环内更新 所有计数器同步维护
标记数组 bool visited[M] = {false}; 只标记,不修改原数组
边界自测 n=1、全0、全最大值 每组都要手动验证

六、总结

一维数组综合应用是从“语法”迈向“算法”的关键台阶。

  1. 三级数组长度必须是编译期常量,不能用变长数组。
  2. 模拟法的核心是“忠实复现规则”,步骤不能少、顺序不能乱。
  3. 枚举法的核心是“遍历所有可能,用条件筛选”。
  4. 统计法的核心是“桶数组计数,一次遍历完成”。
  5. 多条件组合题,所有状态变量必须在循环外初始化。
  6. 最值初值取 arr[0] 或足够大/小的数,不要取0。
  7. 数组遍历统一用 i < n,边界条件是最大的坑。
  8. 每道题写完必须做“3组边界自测”:n=1、全0、全最大值。
  9. 数组遍历题写完后画前3个元素的运行结果,避免下标写反。
  10. 近3场考试难度上浮,细节处理比“能不能做出来”更重要。

复习建议:先把“数组清零”和“智慧购物”两道真题彻底吃透,理解模拟法和统计法的解题框架;再练“多条件统计”类题目,培养一次遍历完成多项任务的能力。每道题做完后,用n=1、全0、全最大值三组数据手动验证,确保边界处理无误。考试中先读懂题目规则,列出所有需要统计的量,再设计循环结构,最后逐行检查边界和输出格式。