2025年第七届全国高校计算机能力挑战赛 决赛 C语言组 编程题汇总

核心总结

A-区间峰值计数 看似基础,实则暗藏效率挑战:nm≥1e5的规模下,需在遍历所有连续区间时动态维护累计和最大值,避免O((nm)^2)的朴素计算超时。掩码运算(511/4095)与峰值范围(±100/±1000)的差异,要求对输入解析和条件判断进行精准适配,属于"基础逻辑+规模优化"的中等难度题目。

B-矩形/正方形字符串摆放的核心难点在于路径模拟的精确性:竖直之字形(B1-B3)需严格控制"下→右→上→右"的方向切换与边界停驻,斜向之字形(B4)则要处理"下→右上→右→左下"的复杂坐标变化,尤其当字符串长度未填满矩阵时,需精准截取有效字符,对空间布局和路径逻辑的拆解能力要求较高,属于中等难度。

C-线段相关问题涵盖多场景算法应用,难度跨度较大:线段重合数(C1-C3)的"事件点扫描法"需精准排序与计数逻辑,线段合并(C5)需处理重叠/相邻边界条件(如首尾差1),而观测点划分(C4)涉及动态规划状态转移(划分k段求最大总长度),需结合预处理过滤(剔除≥2000的点)优化计算,整体属于中等偏上难度。

D-斐波那契频率串判断的关键在于高效校验逻辑:需预生成覆盖大数值(Ci≤1e9)的斐波那契前缀,同时验证字符次数能否完全覆盖前缀元素(允许重复使用次数)。多测试用例(或输入至文件结束)的场景下,需平衡预计算与单次校验效率,对数列生成与匹配策略的设计要求较高,属于中等难度。

E-0/1片段重排匹配的难度体现在匹配规则的差异:子串匹配(E1-E2)需检查连续字符序列,子序列匹配(E3)需验证非连续但顺序一致的字符出现,均需结合0/1数量快速筛选候选排列,并处理重复元素导致的排列去重。n=10时潜在排列数较多,对匹配算法的效率(如子序列的双指针检查)要求提升,属于中等难度。

整体来看,题目核心挑战不仅在于基础算法的应用,更在于大规模数据的效率优化 (如A类的累计和计算)、复杂路径的精准模拟 (如B4的斜向之字形)、多场景算法的灵活切换 (如C类的扫描/DP/合并),以及匹配逻辑的严谨实现(如E类的子串与子序列差异),适合进阶学习者提升算法落地能力。

文章目录

A-区间峰值计数

相似题目差异总结

  • 核心差异:与运算的掩码范围不同(A1、A2使用511m+510,A3使用4095m+4094);峰值判断范围不同(A1、A2为[k-100, k+100],A3为[k-1000, k+1000]);输入格式中参数顺序略有差异(A1为n,m,k,A2、A3为n,k,m),但本质均处理n*m个单元数据。
  • 核心算法:先通过与运算生成实际的单元数组(湿度/功率值),再对每个可能的连续区间,计算从起始位置到区间内各位置的累计和,提取最大值并判断是否在目标范围,统计符合条件的区间数量。
  • 优化点:预处理生成单元数组,避免重复计算;对于每个起始位置,动态维护累计和的最大值,减少重复计算累计和的开销,适配n*m≥1e5的规模。

A1:区间峰值计数(智慧农业场景)

题目描述:你将担任智慧农业平台的数据科学家。在一片自动化管理的农田网格中部署了 n 个土壤湿度传感器(用数组 a 表示),每个传感器实时监测该网格单元的土壤水分含量与作物最适生长标准值的偏离程度。区域的范围是 [1 ~ n]。当系统评估一段连续非空的灌溉单元(也就是一定要选择某些连续的灌溉单元,这些湿度即使都为 0 也算非空单元)时,会计算从单元起始传感器到单元内每个传感器的累计湿度总和,并提取这些累计值中的最大者,作为该单元的峰值水分胁迫指数。只有当某个单元的水分胁迫指数在 [k-100, k+100] 之间时(包含 k-100 和 k+100),才符合 "作物生长适宜区间" 的自动化灌溉标准,求有多少单元符合 "作物生长适宜区间"。例如,每个参考值对 511 二进制 & 之后的湿度为 [0,300,0,200,300] 时,全部可选的区间为 [1,2]、[1,3]、[2,2]、[2,3]、[3,4]、[4,4]、[5,5],它们的水分胁迫指数都在 [200,400] 之间;而区域 [1,4] 的累计湿度值总和分别为 [0,300,300,500],最大值 500 超出范围,故不符合标准。

运行条件

  • 总时限:5000 毫秒
  • 单组时限:1000 毫秒
  • 总内存:320 MB
  • 单组内存:64 MB

输入格式

  • 第一行输入两个整数 n,m,k (1<=n<=5000, 1<=m<=200, 0<=k<=1e6),n 为参考值的数量,k 为临界值。
  • 第二行输入 n 个整数 a1,a2......an (0<=ai<=1e6),代表 n 个参考值。由于输入的限制,这 n 个参考值按顺序对(511 ~ m+510)二进制与(二进制运算符 &,511 是 2 的 9 次方 - 1)后,才是真正的单元湿度。一共 n*m 个单元。例如样例数据 2 中,10 个单元湿度分别是 [0 300 0 100 200 0 0 0 0 0],即 [0&511 300&511 0&511 100&511 200&511 0&512 300&512 0&512 100&512 200&512]。
  • 注意:所有测试数据满足 n*m>=1e5。

输出格式:输出一个整数,代表符合 "作物生长适宜区间" 的区间数量。

输入输出样例

  • 样例1:
    输入

    plaintext 复制代码
    5 1 300
    0 300 0 200 300

    输出

    plaintext 复制代码
    7
  • 样例2:
    输入

    plaintext 复制代码
    5 2 300
    0 300 0 100 200

    输出

    plaintext 复制代码
    24
c 复制代码
#include <stdio.h>

#define MAX_N 5005
#define MAX_LEN 1000005

int a[MAX_N];
long long s[MAX_LEN];   // 前缀和,s[0..len]

int main(void) {
    int n, m;
    long long k;
    // 注意输入顺序:n, m, k
    scanf("%d %d %lld", &n, &m, &k);

    for (int i = 0; i < n; i++) {
        scanf("%d", &a[i]);
    }

    int len = n * m;
    s[0] = 0;
    int idx = 1;

    // 展开 n*m 个单元并做前缀和
    for (int j = 0; j < m; j++) {
        int mask = 511 + j;        // 511 ~ m+510
        for (int i = 0; i < n; i++) {
            s[idx] = s[idx - 1] + (a[i] & mask);
            idx++;
        }
    }

    long long min_v = k - 100;
    long long max_v = k + 100;
    long long ans = 0;

    int l = 1, r = 1;  // 结束位置指针,对应前缀下标

    // i 对应起点 L = i+1,前缀下标用 i
    for (int i = 0; i < len; i++) {
        // 起点右移后,l、r 不能在 i 之前
        if (l < i + 1) l = i + 1;
        if (r < i + 1) r = i + 1;

        // 找到第一个使 s[l] - s[i] >= min_v 的位置
        while (l <= len && s[l] - s[i] < min_v) {
            l++;
        }

        // 保证 r 不小于 l,避免负数区间
        if (r < l) r = l;

        // 找到第一个使 s[r] - s[i] > max_v 的位置
        while (r <= len && s[r] - s[i] <= max_v) {
            r++;
        }

        // 对当前起点 i,对应的合法 R ∈ [l, r-1],个数 = r - l
        if (l <= len) {
            ans += (r - l);
        }
    }

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

A2:区间峰值计数(智慧电网场景)

题目描述:你将担任智慧电网的能源系统分析师。城市供电网络中部署了n个智能监测节点(用数组a表示),每个节点实时记录该区域的用电功率波动值。区域的范围是[1 ~ n]。当系统扫描到一段连续的非空供电区域(也就是一定要选择某些连续的节点,这些功率即使都为0也算非空区域)时,会计算从区域起始节点到区域内每个节点的累计功率总和,并提取这些累计值中的最大者,作为该区域的峰值负载强度。只有当某个区域的峰值负载强度在[k-100,k+100]之间时(包含k-100和k+100),才符合"最优能效区间"的归档标准,求有多少区域符合"最优能效区间"。例如,每个参考值对511二进制&之后的功率为[0,300,0,200,300]时,全部可选的区间为[1,2]、[1,3]、[2,2]、[2,3]、[3,4]、[4,4]、[5,5],它们的峰值负载强度都在[200,400]之间;而区域[1,4]的累计功率总和分别为[0,300,300,500],最大值500超出范围,故不符合标准。

运行条件

  • 总时限:5000 毫秒
  • 单组时限:1000 毫秒
  • 总内存:320 MB
  • 单组内存:64 MB

输入格式

  • 第一行输入两个整数n,k,m(1<=n<=5000,0<k<=1e6,1<=m<=200),n为参考值的数量,k为临界值。
  • 第二行输入n个整数a1,a2...an(0<=ai<=1e6),代表n个参考值。由于输入的限制,这n个参考值按顺序对(511 ~ m+510)二进制与(二进制运算符&,511是2的9次方-1)后,才是真正的节点功率。一共n*m个节点。例如样例数据2中,10个节点功率分别是[0 300 0 100 200 0 0 0 0 0],即[0&511 300&511 0&511 100&511 200&511 0&512 300&512 0&512 100&512 200&512]。
  • 注意:所有测试数据满足n*m>=1e5。

输出格式:输出一个整数,代表符合"最优能效区间"的区间数量。

输入输出样例

  • 样例1:
    输入

    plaintext 复制代码
    5 300 1
    0 300 0 200 300

    输出

    plaintext 复制代码
    7
  • 样例2:
    输入

    plaintext 复制代码
    5 300 2
    0 300 0 100 200

    输出

    plaintext 复制代码
    24
c 复制代码
#include <stdio.h>

#define MAX_N 5005
#define MAX_LEN 1000005

int a[MAX_N];
long long s[MAX_LEN];

int main(void) {
    int n, m;
    long long k;
    // 注意输入顺序:n, k, m
    if (scanf("%d %lld %d", &n, &k, &m) != 3) return 0;

    for (int i = 0; i < n; i++) {
        scanf("%d", &a[i]);
    }

    int len = n * m;
    s[0] = 0;
    int idx = 1;

    // 构造前缀和
    for (int j = 0; j < m; j++) {
        int mask = 511 + j;    // 掩码 511 ~ m+510
        for (int i = 0; i < n; i++) {
            s[idx] = s[idx - 1] + (a[i] & mask);
            idx++;
        }
    }

    long long min_v = k - 100;
    long long max_v = k + 100;
    long long ans = 0;

    int l = 1, r = 1;

    for (int i = 0; i < len; i++) {
        if (l < i + 1) l = i + 1;
        if (r < i + 1) r = i + 1;

        while (l <= len && s[l] - s[i] < min_v) {
            l++;
        }
        if (r < l) r = l;
        while (r <= len && s[r] - s[i] <= max_v) {
            r++;
        }

        if (l <= len) {
            ans += (r - l);
        }
    }

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

A3:区间峰值计数(修正版)

题目描述:你将担任智慧电网的能源系统分析师。城市供电网络中部署了n个智能监测节点(用数组a表示),每个节点实时记录该区域的用电功率波动值。区域的范围是[1 ~n]。当系统扫描到一段连续的非空供电区域(也就是一定要选择某些连续的节点,这些功率即使都为0也算非空区域)时,会计算从区域起始节点到区域内每个节点的累计功率总和,并提取这些累计值中的最大者,作为该区域的峰值负载强度。只有当某个区域的峰值负载强度在[k-1000,k+1000]之间时(包含k-1000和k+1000),才符合"最优能效区间"的归档标准,求有多少区域符合"最优能效区间"。例如,每个参考值对4095二进制&之后的功率为[0,3000,0,2000,3000]时,全部可选的区间为[1,2]、[1,3]、[2,2]、[2,3]、[3,4]、[4,4]、[5,5],它们的峰值负载强度都在[2000,4000]之间;而区域[1,4]的累计功率总和分别为[0,3000,3000,5000],最大值5000超出范围,故不符合标准。

运行条件

  • 总时限:5000 毫秒
  • 单组时限:1000 毫秒
  • 总内存:320 MB
  • 单组内存:64 MB

输入格式

  • 第一行输入两个整数n,k,m(1<=n<=5000,0<=k<=1e6,1<=m<=200),n为参考值的数量,k为临界值。
  • 第二行输入n个整数a1,a2...an(0<=ai<=1e6),代表n个参考值。由于输入的限制,这n个参考值按顺序对(4095 ~ m+4094)二进制与(二进制运算符&,4095是2的12次方-1)后,才是真正的节点功率。一共n*m个节点。例如样例数据2中,10个节点功率分别是[0 3000 0 1000 2000 0 0 0 0 0],即[0&4095 3000&4095 0&4095 1000&4095 2000&4095 0&4096 3000&4096 0&4096 1000&4096 2000&4096]。
  • 注意:所有测试数据满足n*m>=1e5。

输出格式:输出一个整数,代表符合"最优能效区间"的区间数量。

输入输出样例

  • 样例1:
    输入

    plaintext 复制代码
    5 3000 1
    0 3000 0 2000 3000

    输出

    plaintext 复制代码
    7
  • 样例2:
    输入

    plaintext 复制代码
    5 3000 2
    0 3000 0 1000 2000

    输出

    plaintext 复制代码
    24
c 复制代码
#include <stdio.h>

#define MAX_N 5005
#define MAX_LEN 1000005

int a[MAX_N];
long long s[MAX_LEN];

int main(void) {
    int n, m;
    long long k;
    // 输入顺序:n, k, m
    if (scanf("%d %lld %d", &n, &k, &m) != 3) return 0;

    for (int i = 0; i < n; i++) {
        scanf("%d", &a[i]);
    }

    int len = n * m;
    s[0] = 0;
    int idx = 1;

    // 掩码改为 4095 ~ 4095 + (m - 1)
    for (int j = 0; j < m; j++) {
        int mask = 4095 + j;
        for (int i = 0; i < n; i++) {
            s[idx] = s[idx - 1] + (a[i] & mask);
            idx++;
        }
    }

    long long min_v = k - 1000;
    long long max_v = k + 1000;
    long long ans = 0;

    int l = 1, r = 1;

    for (int i = 0; i < len; i++) {
        if (l < i + 1) l = i + 1;
        if (r < i + 1) r = i + 1;

        while (l <= len && s[l] - s[i] < min_v) {
            l++;
        }
        if (r < l) r = l;
        while (r <= len && s[r] - s[i] <= max_v) {
            r++;
        }

        if (l <= len) {
            ans += (r - l);
        }
    }

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

B-矩形竖直之字形字符串摆放

相似题目差异总结

  • 核心差异:摆放路径不同(B1-B3为竖直之字形:先下到第n行→右移→上到第1行→重复;B4为正方形斜向之字形:先下一格→右上到边界→右移→左下到边界→重复);输入格式略有不同(B1含n,m,B2-B3仅含n,B4无行数输入,按字符串长度适配正方形)。
  • 核心算法:模拟字符摆放路径,记录每个位置的字符。对于B1-B3,控制方向(下→上→下...)和列数变化;对于B4,控制方向(下→右上→左下→右上...)和边界判断,最终按行输出字符矩阵。
  • 优化点:使用二维数组存储字符位置,按路径顺序填充,避免越界判断错误;处理字符串长度不足填满矩阵的情况,仅输出已有字符。

B1:矩形竖直之字形字符串摆放(智慧物流机器人场景)

题目描述:智慧物流中心部署了一款自动放货机器人,在多层货架区按照某种路径放置货物。货架区呈长方形布局,共有 n 层(对应 n 行),每层是一个长货架。机器人拣货规则如下:机器人带有所有需要放置的货物,在经过的每一个地方都按顺序放置一个货物。机器人从货架区左上角(第 1 层第 1 列)启动,在这里放置第一个货物。然后下降放货:机器人向下一格移动,然后持续向正下方放货,直到抵达最底层(第 n 行),再往右一格。然后上升放货:机器人向右一格移动至相邻货架列,然后持续向正上方放货,直到抵达顶层(第 1 行),再往右一格。重复上述 2 和 3 的过程,直到所有货物放置完毕,求机器人的放货结果的矩阵。例如,n=3 时,'A' 放在最左上,往下分别放置 'B'、'C'(到达第 3 行),再往右一格是 'D',往上是 'E'、'F'......

运行条件

  • 总时限:5000 毫秒
  • 单组时限:1000 毫秒
  • 总内存:320 MB
  • 单组内存:64 MB

输入格式

  • 第一行是两个整数 n, m(1<=n<=10),表示长方货架范围的宽(行数)和字符串 str 的长度。
  • 第二行是只含大写字母的字符 str(str 长度不大于 100),表示需要放置的货物,按从前往后顺序放置。

输出格式:输出最后放货的矩阵。

输入输出样例

  • 样例1:
    输入

    plaintext 复制代码
    3 8
    ABCDEFJH

    输出

    plaintext 复制代码
    AFJ
    BEH
    CD
  • 样例2:
    输入

    plaintext 复制代码
    3 10
    ABCDEFJHIG

    输出

    plaintext 复制代码
    AFJ
    BEH
    CDIG
c 复制代码
#include <stdio.h>
#include <string.h>

// 最大行列:n<=10,字符串长<=100,列数最多100
#define MAX_ROW 10
#define MAX_COL 100

int main() {
    int n, m;
    char str[101];
    scanf("%d %d", &n, &m);
    scanf("%s", str);
    
    // 初始化矩阵,用空字符填充
    char mat[MAX_ROW][MAX_COL] = {0};
    int row = 0, col = 0;  // 当前位置(行从0开始,对应第1行)
    int dir = 1;           // 方向:1向下,-1向上
    
    for (int i = 0; i < m; i++) {
        mat[row][col] = str[i];  // 放置当前字符
        
        // 计算下一个位置
        if (dir == 1) {
            // 向下移动,若到达最后一行(n-1),则改变方向并右移
            if (row + 1 < n) {
                row++;
            } else {
                dir = -1;  // 改为向上
                col++;     // 右移一列
            }
        } else {
            // 向上移动,若到达第一行(0),则改变方向并右移
            if (row - 1 >= 0) {
                row--;
            } else {
                dir = 1;   // 改为向下
                col++;     // 右移一列
            }
        }
    }
    
    // 输出矩阵,每行输出非空字符
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < MAX_COL; j++) {
            if (mat[i][j] != 0) {
                printf("%c", mat[i][j]);
            } else {
                break;  // 后续无字符,退出当前行
            }
        }
        printf("\n");
    }
    
    return 0;
}

B2:矩形竖直之字形字符串摆放(纯字符串摆放场景)

题目描述:给一个字符串str,将其字母按照以下规则摆放:1. 在一个宽为n的长方形中(n为行数),第一个字符放在最左上角,然后按照路径依次摆放str的其他字符。2. 然后一直往下走,走到第n行停止,再往右一格。3. 然后一直往上走,直到到达第一行,再往右一格。4. 重复以上2和3的过程,直到str的全部字符摆放完成。求最后摆放的图案。例如,n=3时,'A'在最左上,往下分别是'B','C'(到达第3行),再往右一格就是'D',往上是'E','F'......

运行条件

  • 总时限:5000 毫秒
  • 单组时限:1000 毫秒
  • 总内存:320 MB
  • 单组内存:64 MB

输入格式

  • 第一行是一个整数n(1<=n<=10),表示长方形的宽(行数)。
  • 第二行是只含大写字母的字符str(str长度不大于100)。

输出格式:输出最后摆放的图案。

输入输出样例

  • 样例1:
    输入

    plaintext 复制代码
    3
    ABCDEFJH

    输出

    plaintext 复制代码
    AFJ
    BEH
    CD
  • 样例2:
    输入

    plaintext 复制代码
    3
    ABCDEFJHIG

    输出

    plaintext 复制代码
    AFJ
    BEH
    CDIG
c 复制代码
#include <stdio.h>
#include <string.h>

#define MAX_ROW 10
#define MAX_COL 100

int main() {
    int n;
    char str[101];
    scanf("%d", &n);
    scanf("%s", str);
    int m = strlen(str);  // 字符串长度
    
    char mat[MAX_ROW][MAX_COL] = {0};
    int row = 0, col = 0;
    int dir = 1;  // 1向下,-1向上
    
    for (int i = 0; i < m; i++) {
        mat[row][col] = str[i];
        
        // 更新下一个位置
        if (dir == 1) {
            if (row + 1 < n) {
                row++;
            } else {
                dir = -1;
                col++;
            }
        } else {
            if (row - 1 >= 0) {
                row--;
            } else {
                dir = 1;
                col++;
            }
        }
    }
    
    // 输出每行字符
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < MAX_COL; j++) {
            if (mat[i][j] != 0) {
                printf("%c", mat[i][j]);
            } else {
                break;
            }
        }
        printf("\n");
    }
    
    return 0;
}

B3:矩形竖直之字形字符串摆放("20分题"场景)

题目描述:给一个字符串str,将其字母按照以下规则摆放:1. 在一个宽为n的长方形中(n为行数),第一个字符放在最左上角,然后按照路径依次摆放str的其他字符。2. 然后一直往下走,走到第n行停止,再往右一格。3. 然后一直往上走,直到到达第一行,再往右一格。4. 重复以上2和3的过程,直到str的全部字符摆放完成。求最后摆放的图案。例如,n=3时,'A'在最左上,往下分别是'B','C'(到达第3行),再往右一格就是'D',往上是'E','F'......

运行条件

  • 总时限:5000 毫秒
  • 单组时限:1000 毫秒
  • 总内存:320 MB
  • 单组内存:64 MB

输入格式

  • 第一行是一个整数n(1<=n<=10),表示长方形的宽(行数)。
  • 第二行是只含大写字母的字符串str(str长度不大于100)。

输出格式:输出最后摆放的图案。

输入输出样例

  • 样例1:
    输入

    plaintext 复制代码
    3
    ABCDEFJH

    输出

    plaintext 复制代码
    AFJ
    BEH
    CD
  • 样例2:
    输入

    plaintext 复制代码
    3
    ABCDEFJHIG

    输出

    plaintext 复制代码
    AFJ
    BEH
    CDIG
c 复制代码
#include <stdio.h>
#include <string.h>

#define MAX_ROW 10
#define MAX_COL 100

int main() {
    int n;
    char str[101];
    scanf("%d", &n);
    scanf("%s", str);
    int m = strlen(str);
    
    char mat[MAX_ROW][MAX_COL] = {0};
    int row = 0, col = 0;
    int dir = 1;  // 方向:1向下,-1向上
    
    for (int i = 0; i < m; i++) {
        mat[row][col] = str[i];
        
        // 移动逻辑
        if (dir == 1) {
            if (row + 1 < n) {
                row++;
            } else {
                dir = -1;
                col++;
            }
        } else {
            if (row - 1 >= 0) {
                row--;
            } else {
                dir = 1;
                col++;
            }
        }
    }
    
    // 输出矩阵
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < MAX_COL; j++) {
            if (mat[i][j] != 0) {
                printf("%c", mat[i][j]);
            } else {
                break;
            }
        }
        printf("\n");
    }
    
    return 0;
}

B4:正方形斜向之字形字符串摆放(核心场景)

题目描述:给一个字符串str,将其字母按照以下规则在正方形中摆放:1. 第一个字符放在最左上角,然后按照路径依次摆放str的其他字符。2. 往下一格后,一直往右上走,直到到达边界。3. 往右一格后,一直往左下走,直到到达边界。4. 重复以上2和3的过程,直到str的全部字符摆放完成。求最后摆放的图案。例如,样例数据1中,'A'在最左上,往下一格是'B',往右上是'C'(到达边界),再往右一格是'D'......

运行条件

  • 总时限:5000 毫秒
  • 单组时限:1000 毫秒
  • 总内存:320 MB
  • 单组内存:64 MB

输入格式:输入一行只含有大写字母的字符str(str长度不大于100)。

输出格式:输出最后摆放的图案。

输入输出样例

  • 样例1:
    输入

    plaintext 复制代码
    ABCDEFJH

    输出

    plaintext 复制代码
    ACD
    BE
    FH
    J
  • 样例2:
    输入

    plaintext 复制代码
    ABCDEFJHIG

    输出

    plaintext 复制代码
    ACDG
    BEI
    FH
    J
c 复制代码
#include <stdio.h>
#include <string.h>

// 最大正方形边长:字符串长<=100,边长最多100
#define MAX_SIZE 100

int main() {
    char str[101];
    scanf("%s", str);
    int len = strlen(str);
    if (len == 0) {
        return 0;
    }
    
    char mat[MAX_SIZE][MAX_SIZE] = {0};
    int row = 0, col = 0;
    mat[row][col] = str[0];  // 第一个字符放左上角
    
    int step = 1;  // 当前处理的字符索引
    int phase = 0; // 0:先下再右上;1:先右再左下
    
    while (step < len) {
        if (phase == 0) {
            // 阶段0:先向下走一格
            row++;
            if (row < MAX_SIZE && step < len) {
                mat[row][col] = str[step++];
                // 然后往右上走,直到边界
                while (step < len) {
                    row--;
                    col++;
                    // 检查是否出界
                    if (row < 0 || col >= MAX_SIZE) {
                        row++;  // 回退一步
                        col--;
                        break;
                    }
                    mat[row][col] = str[step++];
                }
            }
            phase = 1;  // 切换阶段
        } else {
            // 阶段1:先向右走一格
            col++;
            if (col < MAX_SIZE && step < len) {
                mat[row][col] = str[step++];
                // 然后往左下走,直到边界
                while (step < len) {
                    row++;
                    col--;
                    // 检查是否出界
                    if (row >= MAX_SIZE || col < 0) {
                        row--;  // 回退一步
                        col++;
                        break;
                    }
                    mat[row][col] = str[step++];
                }
            }
            phase = 0;  // 切换阶段
        }
    }
    
    // 输出矩阵,每行非空字符
    for (int i = 0; i < MAX_SIZE; i++) {
        int has_char = 0;
        for (int j = 0; j < MAX_SIZE; j++) {
            if (mat[i][j] != 0) {
                printf("%c", mat[i][j]);
                has_char = 1;
            }
        }
        if (has_char) {
            printf("\n");
        } else {
            // 后续行无字符,退出
            break;
        }
    }
    
    return 0;
}

C-线段最大重合数(会议最大并发数)

相似题目差异总结

  • 核心差异:问题类型不同(C1-C3为求最大线段重合数,C4为观测点划分k段求最大总长度,C5为线段合并后剩余数量);输入格式不同(C1输入至文件结束,C2-C3含n,C4含n,k及观测点坐标,C5含n及线段)。
  • 核心算法:C1-C3采用"事件点扫描法",将线段的起点和终点转为事件,排序后扫描计数最大重合;C4先剔除≥2000的点,再用动态规划或贪心划分k段计算最大总长度;C5先排序线段,再依次合并重叠或相邻线段。
  • 优化点:C1-C3通过排序事件点减少遍历范围;C4预处理过滤无效点,降低数据规模;C5排序后线性扫描合并,提高效率。

C1:线段最大重合数(公司会议场景)

题目描述:公司里有若干会议预订,每个会议都有明确的开始时间和结束时间(比如会议 [3,4] 持续 2 小时,会议包含 3 点和 4 点这两个整点时间),最短也要开 1 小时。现在要找出某一个时间点,最多有多少个会议在同时进行(或者说某时刻需要使用最多的会议室)。求这个最大的并发会议数。例如,样例数据1中,时间点 2 和时间点 3 是会议最密集的时刻,时间点 2 有会议 [1,2] 和会议 [2,3] 同时在开,并发会议数为 2,即为最大值。

运行条件

  • 总时限:5000 毫秒
  • 单组时限:1000 毫秒
  • 总内存:320 MB
  • 单组内存:64 MB

输入格式:输入有若干行,直到输入文件结束。每行两个空格隔开的整数 x, y(1<=x<=y<=10000),表示每个会议的开始时间和结束时间。

输出格式:输出最大的并发会议数。

输入输出样例

  • 样例1:
    输入

    plaintext 复制代码
    1 3
    4 5
    2 3
    7 10

    输出

    plaintext 复制代码
    2
  • 样例2:
    输入

    plaintext 复制代码
    1 2
    3 4
    6 8
    7 9
    5 5

    输出

    plaintext 复制代码
    2
c 复制代码
#include <stdio.h>
#include <stdlib.h>

// 存储事件:时间点和类型(+1开始,-1结束)
typedef struct {
    int time;
    int type;  // 1: 会议开始,-1: 会议结束
} Event;

// 排序函数:按时间升序,时间相同则结束事件(-1)在前
int cmp(const void *a, const void *b) {
    Event *e1 = (Event*)a;
    Event *e2 = (Event*)b;
    if (e1->time != e2->time) {
        return e1->time - e2->time;
    } else {
        // 时间相同,结束事件先处理,避免重复计算
        return e1->type - e2->type;
    }
}

#define MAX_EVENTS 20000  // 最多10000个会议,2*10000个事件

int main() {
    Event events[MAX_EVENTS];
    int count = 0;  // 事件数量
    int x, y;
    
    // 读取所有会议,直到文件结束
    while (scanf("%d %d", &x, &y) == 2) {
        // 会议开始:x时刻+1
        events[count].time = x;
        events[count].type = 1;
        count++;
        // 会议结束:y+1时刻-1(因为会议包含y,y+1开始不包含)
        events[count].time = y + 1;
        events[count].type = -1;
        count++;
    }
    
    // 排序事件
    qsort(events, count, sizeof(Event), cmp);
    
    int max = 0;    // 最大并发数
    int current = 0; // 当前并发数
    for (int i = 0; i < count; i++) {
        current += events[i].type;
        if (current > max) {
            max = current;
        }
    }
    
    printf("%d\n", max);
    return 0;
}

C2:线段最大重合数(线段覆盖场景)

题目描述:有一组线段,它们都有自己的开始点和结束点(比如线段[3,4]的长度是2,线段包含3和4两个点),长度都不会小于1。现在要找到某点,使得该点被最多的线段覆盖(即线段在该点的重合数量最多)。求这个最大的线段重合数。例如,样例数据1中,点2和点3是最大的线段重合点,点2被线段[1,2]和线段[2,3]覆盖,重合数为2,即为最大值。

运行条件

  • 总时限:5000 毫秒
  • 单组时限:1000 毫秒
  • 总内存:320 MB
  • 单组内存:64 MB

输入格式

  • 第一行是一个整数n(1<=n<=50)。
  • 接下来n行,每行两个空格隔开的整数x,y(1<=x<=y<=10000),表示每个线段的开始点和结束点。

输出格式:输出最大线段重合数。

输入输出样例

  • 样例1:
    输入

    plaintext 复制代码
    4
    1 3
    4 5
    2 3
    7 10

    输出

    plaintext 复制代码
    2
  • 样例2:
    输入

    plaintext 复制代码
    5
    1 2
    3 4
    6 8
    7 9
    5 5

    输出

    plaintext 复制代码
    2
c 复制代码
#include <stdio.h>
#include <stdlib.h>

// 事件结构:时间点和类型(+1开始,-1结束)
typedef struct {
    int time;
    int type;
} Event;

// 排序函数
int cmp(const void *a, const void *b) {
    Event *e1 = (Event*)a;
    Event *e2 = (Event*)b;
    if (e1->time != e2->time) {
        return e1->time - e2->time;
    } else {
        return e1->type - e2->type; // 结束事件先处理
    }
}

#define MAX_EVENTS 100  // n<=50,最多100个事件

int main() {
    int n;
    scanf("%d", &n);
    
    Event events[MAX_EVENTS];
    int count = 0;
    int x, y;
    
    for (int i = 0; i < n; i++) {
        scanf("%d %d", &x, &y);
        // 线段开始:x处+1
        events[count].time = x;
        events[count].type = 1;
        count++;
        // 线段结束:y+1处-1
        events[count].time = y + 1;
        events[count].type = -1;
        count++;
    }
    
    qsort(events, count, sizeof(Event), cmp);
    
    int max = 0;
    int current = 0;
    for (int i = 0; i < count; i++) {
        current += events[i].type;
        if (current > max) {
            max = current;
        }
    }
    
    printf("%d\n", max);
    return 0;
}

C3:线段最大重合数("20分题"场景)

题目描述:有一组线段,它们都有自己的开始点和结束点(比如线段[3,4]的长度是2,线段包含3和4两个点),长度都不会小于1。现在要找到某点,使得该点被最多的线段覆盖(即线段在该点的重合数量最多)。求这个最大的线段重合数。例如,样例数据1中,点2和点3是最大的线段重合点,点2被线段[1,2]和线段[2,3]覆盖,重合数为2,即为最大值。

运行条件

  • 总时限:5000 毫秒
  • 单组时限:1000 毫秒
  • 总内存:320 MB
  • 单组内存:64 MB

输入格式

  • 第一行是一个整数n(1<=n<=50)。
  • 接下来n行,每行两个空格隔开的整数x,y(1<=x<=y<=10000),表示每个线段的开始点和结束点。

输出格式:输出最大线段重合数。

输入输出样例

  • 样例1:
    输入

    plaintext 复制代码
    4
    1 3
    4 5
    2 3
    7 10

    输出

    plaintext 复制代码
    2
  • 样例2:
    输入

    plaintext 复制代码
    5
    1 2
    3 4
    6 8
    7 9
    5 5

    输出

    plaintext 复制代码
    2
c 复制代码
#include <stdio.h>
#include <stdlib.h>

typedef struct {
    int time;
    int type; // 1:开始,-1:结束
} Event;

int cmp(const void *a, const void *b) {
    Event *e1 = (Event*)a;
    Event *e2 = (Event*)b;
    if (e1->time != e2->time) {
        return e1->time - e2->time;
    } else {
        return e1->type - e2->type;
    }
}

#define MAX_EVENTS 100

int main() {
    int n;
    scanf("%d", &n);
    
    Event events[MAX_EVENTS];
    int count = 0;
    int x, y;
    
    for (int i = 0; i < n; i++) {
        scanf("%d %d", &x, &y);
        events[count].time = x;
        events[count].type = 1;
        count++;
        events[count].time = y + 1;
        events[count].type = -1;
        count++;
    }
    
    qsort(events, count, sizeof(Event), cmp);
    
    int max = 0, current = 0;
    for (int i = 0; i < count; i++) {
        current += events[i].type;
        if (current > max) {
            max = current;
        }
    }
    
    printf("%d\n", max);
    return 0;
}

C4:动物观测点划分k段(核心场景)

题目描述 :在一个自然保护区内,科研人员设置了n个动物观测点,沿直线分布且坐标按从小到大排列(无重复)。需将观测点划分为k个连续区域(通过选择k-1个相邻间隔分割),每个区域的活动范围长度为区域内最后一个与第一个观测点的坐标差值。要求所有研究区域的活动范围总长度尽可能大,且只考虑坐标小于2000的观测点(≥2000需剔除)。计算最优划分下的最大总长度。

运行条件

  • 总时限:5000 毫秒
  • 单组时限:1000 毫秒
  • 总内存:320 MB
  • 单组内存:64 MB

输入格式

  • 第一行包含两个正整数n和k(2 ≤ k ≤ n ≤ 15000)。
  • 第二行包含n个整数,为按升序排列的观测点坐标(长整数,无重复)。

输出格式:输出一行整数,表示最大总活动范围长度。

输入输出样例

  • 样例1:
    输入

    plaintext 复制代码
    20 4
    3 4 6 8 14 15 16 17 21 25 26 27 30 31 40 41 42 43 2100 2300

    输出

    plaintext 复制代码
    37
  • 样例2:
    输入

    plaintext 复制代码
    2 2
    10 20

    输出

    plaintext 复制代码
    0
c 复制代码
#include <stdio.h>
#include <stdlib.h>

#define MAX_N 15000

// 比较函数,用于 qsort
int compare_long_long(const void *a, const void *b) {
    long long x = *(const long long *)a;
    long long y = *(const long long *)b;
    if (x < y) return -1;
    if (x > y) return 1;
    return 0;
}

int main() {
    int n, k;
    scanf("%d %d", &n, &k);
    
    long long coords[MAX_N];
    for (int i = 0; i < n; i++) {
        scanf("%lld", &coords[i]);
    }
    
    // 1. 筛选出坐标小于2000的观测点
    int m = 0;
    long long valid[MAX_N];
    for (int i = 0; i < n; i++) {
        if (coords[i] < 2000) {
            valid[m++] = coords[i];
        }
    }
    
    // 2. 检查可行性
    if (m < k) {
        printf("0\n");
        return 0;
    }
    
    // 3. 计算相邻间隔
    long long gaps[MAX_N];
    int gap_count = m - 1;
    for (int i = 0; i < gap_count; i++) {
        gaps[i] = valid[i + 1] - valid[i];
    }
    
    // 4. 使用快速排序对间隔进行排序
    qsort(gaps, gap_count, sizeof(long long), compare_long_long);
    
    // 5. 计算最小的 k-1 个间隔之和
    long long min_gaps_sum = 0;
    // 注意:当 k=1 时,不需要减去任何间隔
    for (int i = 0; i < k - 1 && i < gap_count; i++) {
        min_gaps_sum += gaps[i];
    }
    
    // 6. 计算最大总长度
    long long max_total = (valid[m - 1] - valid[0]) - min_gaps_sum;
    printf("%lld\n", max_total);
    
    return 0;
}

C5:线段合并后剩余条数(核心场景)

题目描述:有一组线段,它们都有自己的开始点和结束点(比如线段[3,4]的长度是2,线段包含3和4两个点),长度都不会小于1。现在要合并这些线段,规则为:线段部分相重或者首尾相差为1的两个线段可以合并。求所有能合并的线段都合并后,剩余的线段数量。例如,样例数据1中,线段[1,3]和[2,3]部分相重,合并为[1,3];合并后的[1,3]与[4,5]首尾相差1(3和4),合并为[1,5];最终剩余[1,5]和[7,10]两条线段,答案为2。

运行条件

  • 总时限:5000 毫秒
  • 单组时限:1000 毫秒
  • 总内存:320 MB
  • 单组内存:64 MB

输入格式

  • 第一行是一个整数n(1<=n<=50)。
  • 接下来n行,每行两个空格隔开的整数x,y(1<=x<=y<=10000),表示每个线段的开始点和结束点。

输出格式:输出合并后线段的数量。

输入输出样例

  • 样例1:
    输入

    plaintext 复制代码
    4
    1 3
    4 5
    2 3
    7 10

    输出

    plaintext 复制代码
    2
c 复制代码
#include <stdio.h>
#include <stdlib.h>

// 线段结构
typedef struct {
    int x;
    int y;
} Segment;

// 按线段起点排序
int cmp(const void *a, const void *b) {
    Segment *s1 = (Segment*)a;
    Segment *s2 = (Segment*)b;
    return s1->x - s2->x;
}

#define MAX_N 50

int main() {
    int n;
    scanf("%d", &n);
    
    Segment segs[MAX_N];
    for (int i = 0; i < n; i++) {
        scanf("%d %d", &segs[i].x, &segs[i].y);
    }
    
    // 排序线段
    qsort(segs, n, sizeof(Segment), cmp);
    
    // 合并线段
    int count = 0;  // 合并后线段数量
    Segment merged[MAX_N];
    
    if (n == 0) {
        printf("0\n");
        return 0;
    }
    
    // 初始化第一条合并线段
    merged[0] = segs[0];
    count = 1;
    
    for (int i = 1; i < n; i++) {
        Segment *last = &merged[count - 1];
        Segment *curr = &segs[i];
        
        // 检查是否可以合并:当前线段起点 <= 上一条终点 + 1
        if (curr->x <= last->y + 1) {
            // 合并后的终点取最大值
            if (curr->y > last->y) {
                last->y = curr->y;
            }
        } else {
            // 不能合并,新增一条
            merged[count] = *curr;
            count++;
        }
    }
    
    printf("%d\n", count);
    return 0;
}

D-斐波那契频率串判断

相似题目差异总结

  • 核心差异:输入格式不同(D1、D2含t组测试用例,D3输入至文件结束),但问题本质一致:判断字符出现次数能否匹配斐波那契数列的某段前缀(频律数组)。
  • 核心算法:计算斐波那契数列前缀,检查前缀和是否等于字符总次数,且前缀中的每个数都能被给定的字符出现次数覆盖(次数需包含前缀所有元素,允许重复使用次数)。
  • 优化点:预先生成足够长的斐波那契数列(因Ci≤1e9,前缀长度有限),减少重复计算;对字符次数排序,便于快速匹配前缀元素。

D1:斐波那契频率串判断(星辉密语场景)

题目描述:在"星辉密语"的遗迹里,篆刻着一条唯有"频律者"才能听懂的规则:把一串"符纹"从头至尾展开,相邻且相同的符纹会累加起来,最后形成的数组,就是这条符纹串的频律数组。比如符纹"abccdddaaaaa"的频律数组就是[1,1,2,3,5](一个符纹产生的频律数组是唯一的),对应1个'a',1个'b',2个'c',3个'd',5个'a'。若频律数组恰好是斐波那契数列的某段前缀,则这串符纹被冠以"黄金脉律"之称。现已知符纹中每种字符的出现次数(如[2,3,1]表示某种字符2个、另一种3个、第三种1个),但未知字符种类和排列顺序,判断该符纹是否有可能是"黄金脉律"。有可能输出YES,否则输出NO。斐波那契数列定义为:f[1] = 1, f[2] = 1, f[i] = f[i-1] + f[i-2](i ≥ 3)。例如,测试数据中"3;1 2 4"表示三种字符各1、2、4个,若排列为"ACBBCCCC",频律数组为[1,1,2,3],符合斐波那契前缀,输出YES;而"1;2"表示一种字符2个,排列必为"AA",频律数组[2],不符合,输出NO。

运行条件

  • 总时限:5000 毫秒
  • 单组时限:1000 毫秒
  • 总内存:320 MB
  • 单组内存:64 MB

输入格式

  • 第一行包含一个整数t(1<<t<1e4),表示测试用例的数量。
  • 每个测试用例的第一行包含一个整数k(1<=k<=100),表示字符种类数。
  • 每个测试用例的第二行包含k个整数C1、C2、...Ck(1<Ci<1e9),表示每种字符的出现次数。

输出格式:如果该符纹有可能是"黄金脉律",输出"YES",否则输出"NO"。

输入输出样例

  • 样例1:
    输入

    plaintext 复制代码
    3
    1
    1
    2
    1 1
    2
    2 1

    输出

    plaintext 复制代码
    YES
    YES
    NO
  • 样例2:
    输入

    plaintext 复制代码
    3
    2
    2 2
    1
    2
    3
    1 2 4

    输出

    plaintext 复制代码
    NO
    NO
    YES
c 复制代码
#include <stdio.h>

#define MAXK 100
#define MAXF 100

int main(void) {
    long long fib[MAXF];
    int fib_len;

    // 预生成足够长的斐波那契前缀(F0 = F1 = 1)
    fib[0] = 1;
    fib[1] = 1;
    fib_len = 2;
    while (1) {
        long long next = fib[fib_len - 1] + fib[fib_len - 2];
        if (next > 1000000000000000000LL) break; // 防止溢出
        fib[fib_len++] = next;
        if (fib_len >= MAXF) break;
    }

    int t;
    if (scanf("%d", &t) != 1) return 0;
    while (t--) {
        int k;
        long long c[MAXK];
        long long sum = 0;

        if (scanf("%d", &k) != 1) return 0;
        for (int i = 0; i < k; i++) {
            scanf("%lld", &c[i]);
            sum += c[i];
        }

        // 找到使得斐波那契前缀和等于 sum 的最大下标 m
        long long pref = 0;
        int m = -1;
        for (int i = 0; i < fib_len; i++) {
            pref += fib[i];
            if (pref == sum) {
                m = i;
                break;
            }
            if (pref > sum) break;
        }

        if (m == -1) {
            puts("NO");
            continue;
        }

        int prev = -1; // 上一次使用的字符下标
        int ok = 1;
        // 从最大的 fib[m] 往下贪心取
        for (int i = m; i >= 0; i--) {
            int best_idx = -1;
            long long best_val = -1;
            // 选取当前剩余次数最大的、且不是前一个块用的字符
            for (int j = 0; j < k; j++) {
                if (j == prev) continue;
                if (c[j] > best_val) {
                    best_val = c[j];
                    best_idx = j;
                }
            }
            if (best_idx == -1 || c[best_idx] < fib[i]) {
                ok = 0;
                break;
            }
            c[best_idx] -= fib[i];
            prev = best_idx;
        }

        puts(ok ? "YES" : "NO");
    }

    return 0;
}

D2:斐波那契频率串判断(斐波那契数列字符串场景)

题目描述:对于一个字符串,将其相邻的相同字符数量按顺序统计,形成的数列称为该字符串的统计数列(唯一)。例如字符串"abccdddaaaaa"的统计数列是[1,1,2,3,5](1个'a',1个'b',2个'c',3个'd',5个'a')。若统计数列是斐波那契数列的某段前缀,则称该字符串为斐波那契数列字符串。现已知字符串中每种字符的出现次数(如[2,3,1]表示某种字符2个、另一种3个、第三种1个),但未知字符种类和排列顺序,判断该字符串是否有可能是斐波那契数列字符串。有可能输出YES,否则输出NO。斐波那契数列定义为:f[1] = 1, f[2] = 1, f[i] = f[i-1] + f[i-2](i ≥ 3)。例如,测试数据中"3;1 2 4"表示三种字符各1、2、4个,若排列为"ACBBCCCC",统计数列[1,1,2,3],符合斐波那契前缀,输出YES;而"1;2"表示一种字符2个,排列必为"AA",统计数列[2],不符合,输出NO。

运行条件

  • 总时限:5000 毫秒
  • 单组时限:1000 毫秒
  • 总内存:320 MB
  • 单组内存:64 MB

输入格式

  • 第一行包含一个整数t(1<<t<1e4),表示测试用例的数量。
  • 每个测试用例的第一行包含一个整数k(1<=k<=100),表示字符种类数。
  • 每个测试用例的第二行包含k个整数c1、c2...ck,表示每种字符的出现次数。

输出格式:如果该字符串有可能是斐波那契数列字符串,输出"YES",否则输出"NO"。

输入输出样例

  • 样例1:
    输入

    plaintext 复制代码
    3
    1
    1
    2
    1 1
    2
    2 1

    输出

    plaintext 复制代码
    YES
    YES
    NO
  • 样例2:
    输入

    plaintext 复制代码
    3
    2
    2 2
    1
    2
    3
    1 2 4

    输出

    plaintext 复制代码
    NO
    NO
    YES
c 复制代码
#include <stdio.h>

#define MAXK 100
#define MAXF 100

int main(void) {
    long long fib[MAXF];
    int fib_len;

    fib[0] = 1;
    fib[1] = 1;
    fib_len = 2;
    while (1) {
        long long next = fib[fib_len - 1] + fib[fib_len - 2];
        if (next > 1000000000000000000LL) break;
        fib[fib_len++] = next;
        if (fib_len >= MAXF) break;
    }

    int t;
    if (scanf("%d", &t) != 1) return 0;
    while (t--) {
        int k;
        long long c[MAXK];
        long long sum = 0;

        if (scanf("%d", &k) != 1) return 0;
        for (int i = 0; i < k; i++) {
            scanf("%lld", &c[i]);
            sum += c[i];
        }

        long long pref = 0;
        int m = -1;
        for (int i = 0; i < fib_len; i++) {
            pref += fib[i];
            if (pref == sum) {
                m = i;
                break;
            }
            if (pref > sum) break;
        }

        if (m == -1) {
            puts("NO");
            continue;
        }

        int prev = -1;
        int ok = 1;
        for (int i = m; i >= 0; i--) {
            int best_idx = -1;
            long long best_val = -1;
            for (int j = 0; j < k; j++) {
                if (j == prev) continue;
                if (c[j] > best_val) {
                    best_val = c[j];
                    best_idx = j;
                }
            }
            if (best_idx == -1 || c[best_idx] < fib[i]) {
                ok = 0;
                break;
            }
            c[best_idx] -= fib[i];
            prev = best_idx;
        }

        puts(ok ? "YES" : "NO");
    }

    return 0;
}

D3:斐波那契频率串判断(无声列车场景)

题目描述:在"鲸落云轨"上,"无声列车"的光带字符串按规则形成"轨律":将光带中相同且相邻的灯粒合并,记录合并后的长度顺序,即为轨律数组(唯一)。例如光带字符串"abccdddaaaaaa"的轨律数组是[1,1,2,3,5](1个'a',1个'b',2个'c',3个'd',5个'a')。若轨律数组是斐波那契数列的某段前缀,则该列车为"黄金班次"。现已知光带中每种颜色灯粒的出现次数(如[2,3,1]表示某种颜色2粒、另一种3粒、第三种1粒),但未知颜色种类和排列顺序,判断是否存在一种排布使列车成为"黄金班次"。存在输出YES,否则输出NO。斐波那契数列定义为:f[1] = 1, f[2] = 1, f[i] = f[i-1] + f[i-2](i ≥ 3)。例如,测试数据中"3;1 2 4"表示三种颜色各1、2、4粒,若排列为"ACBBCCCC",轨律数组[1,1,2,3],符合斐波那契前缀,输出YES;而"1;2"表示一种颜色2粒,排列必为"AA",轨律数组[2],不符合,输出NO。

运行条件

  • 总时限:5000 毫秒
  • 单组时限:1000 毫秒
  • 总内存:320 MB
  • 单组内存:64 MB

输入格式:输入有若干组,直到文件结束。每组测试用例的第一行包含一个整数k(1<=k<=100),表示灯粒颜色种类数。每个测试用例的第二行包含k个整数c1、c2、...ck(1<ci<1e9),表示每种颜色灯粒的出现次数。

输出格式:如果该列车有可能是"黄金班次",输出"YES",否则输出"NO"。

输入输出样例

  • 样例1:
    输入

    plaintext 复制代码
    3
    1
    1
    2
    1 1
    2
    2 1

    输出

    plaintext 复制代码
    YES
    YES
    NO
  • 样例2:
    输入

    plaintext 复制代码
    3
    2
    2 2
    1
    2
    3
    1 2 4

    输出

    plaintext 复制代码
    NO
    NO
    YES
c 复制代码
#include <stdio.h>

#define MAXK 100
#define MAXF 100

int main(void) {
    long long fib[MAXF];
    int fib_len;

    // 斐波那契:F0 = F1 = 1
    fib[0] = 1;
    fib[1] = 1;
    fib_len = 2;
    while (1) {
        long long next = fib[fib_len - 1] + fib[fib_len - 2];
        if (next > 1000000000000000000LL) break;
        fib[fib_len++] = next;
        if (fib_len >= MAXF) break;
    }

    int k;
    while (scanf("%d", &k) == 1) {
        long long c[MAXK];
        long long sum = 0;
        for (int i = 0; i < k; i++) {
            scanf("%lld", &c[i]);
            sum += c[i];
        }

        long long pref = 0;
        int m = -1;
        for (int i = 0; i < fib_len; i++) {
            pref += fib[i];
            if (pref == sum) {
                m = i;
                break;
            }
            if (pref > sum) break;
        }

        if (m == -1) {
            puts("NO");
            continue;
        }

        int prev = -1;
        int ok = 1;
        for (int i = m; i >= 0; i--) {
            int best_idx = -1;
            long long best_val = -1;
            for (int j = 0; j < k; j++) {
                if (j == prev) continue;
                if (c[j] > best_val) {
                    best_val = c[j];
                    best_idx = j;
                }
            }
            if (best_idx == -1 || c[best_idx] < fib[i]) {
                ok = 0;
                break;
            }
            c[best_idx] -= fib[i];
            prev = best_idx;
        }

        puts(ok ? "YES" : "NO");
    }

    return 0;
}

E-0/1片段重排为给定串的子串

相似题目差异总结

  • 核心差异:匹配类型不同(E1-E2为判断重排后是否为子串,E3为判断是否为子序列);输入格式略有不同(E1含n,m,E2仅含n),但均需处理0/1片段的重排组合。
  • 核心算法:先统计0和1的数量,生成所有去重的重排字符串;E1-E2检查这些字符串是否为str的子串,E3检查是否为子序列;根据有效字符串的重复排列数(考虑0/1的重复个数)计算总组合数。
  • 优化点:利用0和1的数量快速筛选可能的子串/子序列(如子串需含相同数量的0和1);对str预处理(如记录0/1位置),加速匹配判断。

E1:0/1片段重排为给定串的子串(普通01串场景)

题目描述:现有n个数字(均为0或1)和一个长度为m的01串str。将这n个数字交换顺序后组合成新字符串,若该新字符串是str的某个子串(子串指字符串中任意连续的一段字符),则该组合满足要求。求满足要求的组合数。例如,样例数据1中,n=3的数字[1,0,1]可组成6种字符串:"011"、"011"、"101"、"101"、"110"、"110",其中"101"是str="0101"的子串,满足要求的组合数为2。

运行条件

  • 总时限:5000 毫秒
  • 单组时限:1000 毫秒
  • 总内存:320 MB
  • 单组内存:64 MB

输入格式

  • 第一行是两个整数n,m(1<=n<=10,1<=m<=500)。
  • 第二行是一个长度为m的01串str。
  • 第三行是长度为n的数字(仅含0或1),用空格隔开。

输出格式:输出满足要求的组合数。

输入输出样例

  • 样例1:
    输入

    plaintext 复制代码
    3 4
    0101
    1 0 1

    输出

    plaintext 复制代码
    2
  • 样例2:
    输入

    plaintext 复制代码
    3 4
    0110
    1 0 1

    输出

    plaintext 复制代码
    4
c 复制代码
#include <stdio.h>
#include <string.h>

int n, m;
char str_s[501];
int cnt0, cnt1;
char temp_s[11];
int pattern_cnt;
long long fact[11];

int exists_substring(const char *s, int m, const char *pat, int len) {
    if (m < len) return 0;
    for (int i = 0; i <= m - len; i++) {
        int ok = 1;
        for (int j = 0; j < len; j++) {
            if (s[i + j] != pat[j]) {
                ok = 0;
                break;
            }
        }
        if (ok) return 1;
    }
    return 0;
}

void dfs(int pos, int z, int o) {
    if (pos == n) {
        if (z == 0 && o == 0) {
            temp_s[n] = '\0';
            if (exists_substring(str_s, m, temp_s, n)) {
                pattern_cnt++;
            }
        }
        return;
    }
    if (z > 0) {
        temp_s[pos] = '0';
        dfs(pos + 1, z - 1, o);
    }
    if (o > 0) {
        temp_s[pos] = '1';
        dfs(pos + 1, z, o - 1);
    }
}

int main(void) {
    // 阶乘 0..10
    fact[0] = 1;
    for (int i = 1; i <= 10; i++) fact[i] = fact[i - 1] * i;

    if (scanf("%d %d", &n, &m) != 2) return 0;
    scanf("%s", str_s);

    cnt0 = cnt1 = 0;
    for (int i = 0; i < n; i++) {
        int d;
        scanf("%d", &d);
        if (d == 0) cnt0++;
        else cnt1++;
    }

    pattern_cnt = 0;
    dfs(0, cnt0, cnt1);

    long long per = fact[cnt0] * fact[cnt1];
    long long ans = per * pattern_cnt;

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

E2:0/1片段重排为给定串的子串(基因工程场景)

题目描述:在基因工程实验室中,研究人员有n个基因片段(0表示正常基因片段,1表示突变基因片段)和一个主基因序列str(由0和1组成)。将n个基因片段交换顺序后组合成新基因片段,若该新片段是主基因序列的子串(子串指字符串中任意连续的一段字符),则该排列可通过验证。求能通过验证的组合数。例如,样例数据1中,n=3的基因片段[1,0,1]可组成6种字符串:"011"、"011"、"101"、"101"、"110"、"110",其中"101"是str="0101"的子串,通过验证的组合数为2。

运行条件

  • 总时限:5000 毫秒
  • 单组时限:1000 毫秒
  • 总内存:320 MB
  • 单组内存:64 MB

输入格式

  • 第一行是一个整数n(1<=n<=10)。
  • 第二行是一个01字符串str(长度不大于500)。
  • 第三行是长度为n的数字(仅含0或1),用空格隔开,表示基因片段。

输出格式:输出满足要求的组合数。

输入输出样例

  • 样例1:
    输入

    plaintext 复制代码
    3
    0101
    1 0 1

    输出

    plaintext 复制代码
    2
  • 样例2:
    输入

    plaintext 复制代码
    3
    0110
    1 0 1

    输出

    plaintext 复制代码
    4
c 复制代码
#include <stdio.h>
#include <string.h>

int n, m;
char str_s[501];
int cnt0, cnt1;
char temp_s[11];
int pattern_cnt;
long long fact[11];

int exists_substring(const char *s, int m, const char *pat, int len) {
    if (m < len) return 0;
    for (int i = 0; i <= m - len; i++) {
        int ok = 1;
        for (int j = 0; j < len; j++) {
            if (s[i + j] != pat[j]) {
                ok = 0;
                break;
            }
        }
        if (ok) return 1;
    }
    return 0;
}

void dfs(int pos, int z, int o) {
    if (pos == n) {
        if (z == 0 && o == 0) {
            temp_s[n] = '\0';
            if (exists_substring(str_s, m, temp_s, n)) {
                pattern_cnt++;
            }
        }
        return;
    }
    if (z > 0) {
        temp_s[pos] = '0';
        dfs(pos + 1, z - 1, o);
    }
    if (o > 0) {
        temp_s[pos] = '1';
        dfs(pos + 1, z, o - 1);
    }
}

int main(void) {
    fact[0] = 1;
    for (int i = 1; i <= 10; i++) fact[i] = fact[i - 1] * i;

    if (scanf("%d", &n) != 1) return 0;
    scanf("%s", str_s);
    m = (int)strlen(str_s);

    cnt0 = cnt1 = 0;
    for (int i = 0; i < n; i++) {
        int d;
        scanf("%d", &d);
        if (d == 0) cnt0++;
        else cnt1++;
    }

    pattern_cnt = 0;
    dfs(0, cnt0, cnt1);

    long long per = fact[cnt0] * fact[cnt1];
    long long ans = per * pattern_cnt;

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

E3:0/1片段重排为给定串的子序列(普通场景)

题目描述:现有n个数字(均为0或1)和一个长度为m的01串str。将这n个数字交换顺序后组合成新字符串,若该新字符串是str的某个子序列(子序列指从原序列中按原有顺序、不必然连续地取出元素组成的新序列),则该组合满足要求。求满足要求的组合数。例如,样例数据1中,n=3的数字[1,0,1]可组成6种字符串:"011"、"011"、"101"、"101"、"110"、"110",其中"011"、"011"、"101"、"101"是str="0101"的子序列,满足要求的组合数为4。

运行条件

  • 总时限:5000 毫秒
  • 单组时限:1000 毫秒
  • 总内存:320 MB
  • 单组内存:64 MB

输入格式

  • 第一行是两个整数n,m(1<=n<=10,1<=m<=500)。
  • 第二行是一个长度为m的01串str。
  • 第三行是长度为n的数字(仅含0或1),用空格隔开。

输出格式:输出满足要求的组合数。

输入输出样例

  • 样例1:
    输入

    plaintext 复制代码
    3 4
    0101
    1 0 1

    输出

    plaintext 复制代码
    4
  • 样例2:
    输入

    plaintext 复制代码
    3 4
    0110
    1 0 1

    输出

    plaintext 复制代码
    4
c 复制代码
#include <stdio.h>
#include <string.h>

int n, m;
char str_s[501];
int cnt0, cnt1;
char temp_s[11];
int pattern_cnt;
long long fact[11];

int exists_subsequence(const char *s, int m, const char *pat, int len) {
    int i = 0, j = 0;
    while (i < m && j < len) {
        if (s[i] == pat[j]) {
            i++;
            j++;
        } else {
            i++;
        }
    }
    return j == len;
}

void dfs(int pos, int z, int o) {
    if (pos == n) {
        if (z == 0 && o == 0) {
            temp_s[n] = '\0';
            if (exists_subsequence(str_s, m, temp_s, n)) {
                pattern_cnt++;
            }
        }
        return;
    }
    if (z > 0) {
        temp_s[pos] = '0';
        dfs(pos + 1, z - 1, o);
    }
    if (o > 0) {
        temp_s[pos] = '1';
        dfs(pos + 1, z, o - 1);
    }
}

int main(void) {
    fact[0] = 1;
    for (int i = 1; i <= 10; i++) fact[i] = fact[i - 1] * i;

    if (scanf("%d %d", &n, &m) != 2) return 0;
    scanf("%s", str_s);

    cnt0 = cnt1 = 0;
    for (int i = 0; i < n; i++) {
        int d;
        scanf("%d", &d);
        if (d == 0) cnt0++;
        else cnt1++;
    }

    pattern_cnt = 0;
    dfs(0, cnt0, cnt1);

    long long per = fact[cnt0] * fact[cnt1];
    long long ans = per * pattern_cnt;

    printf("%lld\n", ans);
    return 0;
}
相关推荐
沐知全栈开发几秒前
jQuery 杂项方法
开发语言
wregjru8 分钟前
【C++】2.6 红黑树及其实现(附代码)
开发语言·c++
2501_9462447818 分钟前
Flutter & OpenHarmony OA系统设置页面组件开发指南
开发语言·javascript·flutter
一分半心动21 分钟前
清理C盘的python脚本
开发语言·python
一只鹿鹿鹿1 小时前
网络信息与数据安全建设方案
大数据·运维·开发语言·网络·mysql
a努力。1 小时前
国家电网Java面试被问:慢查询的优化方案
java·开发语言·面试
Dillon Dong1 小时前
从C到Simulink: ARM Compiler 5 (RVDS) 为什么simulink 不能使用arm编译
c语言·arm开发·simulink
@小码农1 小时前
202512 电子学会 Scratch图形化编程等级考试四级真题(附答案)
java·开发语言·算法
ejjdhdjdjdjdjjsl1 小时前
C#类型转换与异常处理全解析
开发语言·c#
qq_336313931 小时前
java基础-IO流(转换流)
java·开发语言·python