C语言之可分解的正整数(蓝桥杯省B)

题目描述

定义一种特殊的整数序列,这种序列由连续递增的整数组成,并满足以下条件:

  1. 序列长度至少为 3。
  2. 序列中的数字是连续递增的整数(即相邻元素之差为 1),可以包括正整数、负整数或 0。

例如,[1,2,3]、[4,5,6,7] 和 [−1,0,1] 是符合条件的序列,而 [1,2](长度不足)和 [1,2,4](不连续)不符合要求。

现给定一组包含 N 个正整数的数据 A1​,A2​,...,AN​。如果某个 Ai​ 能够表示为符合上述条件的连续整数序列中所有元素的和,则称 Ai​ 是可分解的。

请你统计这组数据中可分解的正整数的数量。

输入格式

输入的第一行包含一个正整数 N,表示数据的个数。

第二行包含 N 个正整数 A1​,A2​,...,AN​,表示需要判断是否可分解的正整数序列。

输出格式

输出一个整数,表示给定数据中可分解的正整数的数量。

输入输出样例

输入

复制代码
3
3 6 15

输出

复制代码
3

说明/提示

样例说明

  • Ai=3 是可分解的,因为 [0,1,2] 的和为 0+1+2=3。
  • Ai=6 是可分解的,因为 [1,2,3] 的和为 1+2+3=6。
  • Ai=15 是可分解的,因为 [4,5,6] 的和为 4+5+6=15。

所以可分解的正整数的数量为 3。

评测用例规模与约定

  • 对于 30% 的评测用例,1≤N≤100,1≤Ai≤100。
  • 对于 100% 的评测用例,1≤N≤105,1≤Ai≤109。
cs 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

int is_decomposable(int S) {
    long long T = 2LL * S;//S表示的是正整数,是一个和,所以可能会很大,最好要用long long类型
    // 枚举 n 为 T 的约数
    for (long long n = 1; n * n <= T; n++) {
        if (T % n != 0) continue;
        // 检查 n 作为长度
        if (n >= 3) {
            long long m = T / n;
            if ((m-n+1) % 2 == 0) { //必须保证k是整数
                return 1;
            }
        }
        // 检查 T/n 作为长度
        long long n2 = T / n;
        if (n2 >= 3 && n2 != n) {
            long long m2 = n; // 因为 m2 = T/n2 = n
            if ((m2-n2+1) % 2 == 0) {
                return 1;
            }
        }
    }
    return 0;
}

int main() {
    int N;
    scanf("%d", &N);
    int count = 0;
    for (int i = 0; i < N; i++) {
        int A;
        scanf("%d", &A);//输入要证明的正整数
        if (is_decomposable(A)) {//用于证明是可分解的正整数,是返回1,不是返回0
            count++;
        }
    }
    printf("%d\n", count);
    return 0;
}

上述代码和"最大公约数和最小公倍数"那道题目是一样的,其实比那道题目更简单。主要在这些方面是一样的:1.数论推导,推导出几个变量之间的关系,通过这些关系简化过2.is_decomposable函数中for循环和if语句几乎和那道题目是一样的。下面我给出为什么两道题目这么像......

整体思路:主要是数论推导

1.问题转化

对于给定的整数 S,检查是否存在至少 3 个连续正整数之和等于 S。

设连续正整数序列为:k, k+1, ..., k+(n-1),其中 n≥3(至少 3 个数)

复制代码
S = k + (k+1) + ... + (k+n-1)

在序列 k, k+1, ..., k+(n-1) 中:

  • k 是序列的起始值(第一个数)

  • n 是序列中整数的个数

举个例子:

  • 如果 n=3,表示有 3 个连续整数

  • 如果 k=2,那么序列是:2, 3, 4(共 3 个数)

  • 最后一个数是 k+(n-1) = 2+(3-1) = 4

2.数学公式推导

等差数列求和:S = n * [k + (k+n-1)] / 2 = n * (2k + n - 1) / 2

整理得:2S = n * (2k + n - 1)

令n = 连续整数的个数(≥3), m = 2k + n - 1,则n * m = 2S且k = (m - n + 1) / 2

因为两道题目都可以写成某两个数乘积等于一个常数的形式,而且都是一步一步推导得到的乘积形式,所以说二者真的很像。

注意!!!一定要对所有出现的变量整出个式子来表示它,然后根据这个式子才能写出它必须要满足的条件,否则光凭眼睛看,条件肯定是不全的。

3.解的判断条件

k是序列的起始值,即第一个数,所以必须是正整数,既然这样,那么(m-n+1)必须是偶数,可以自己举例,而这个偶数的实现就是可以整除2没有余数。

4.解析以下下面代码

// 检查 T/n 作为长度

long long n2 = T / n;

if (n2 >= 3 && n2 != n) {

long long m2 = n; // 因为 m2 = T/n2 = n

if ((m2-n2+1) % 2 == 0) {

return 1;

}

}

为什么要写上述这个代码呢?

例子:T=12

约数有:1, 2, 3, 4, 6, 12

当 n=1 时,n2=12

当 n=2 时,n2=6

当 n=3 时,n2=4

如果我们只检查 n≤√12≈3.46 的情况:

  • n=1: 检查 n=1(跳过,因为 n<3)

  • n=2: 检查 n=2(跳过,因为 n<3)

  • n=3: 检查 n=3

这样就会错过 n2=4, 6, 12 这些情况!而这些情况是更符合题意的。

那为什么不直接n<T呢,这样直接包含所有情况?

因为原代码的方法最多循环 √(2×10^6) ≈ 1414 次,而且一般都是按原代码的方法进行求解的。

为什么这道题目考虑到要这样做呢?

其实很难想到。所以观察一下题目要求吧。

要求找到可分解正整数,但是如果直接写,直接遍历,根本无法保证是分解成三个及以上的正整数,所以不能直接写。

如果考虑到"最大公约数和最小公倍数"问题要求,就会知道,这两道题目要求都有一点相似。"最大公约数和最小公倍数"要求满足下列两个条件的正整数个数,而这道题目要求满足下列两个条件的可分解正整数个数。"最大公约数和最小公倍数"是根据数学推论写出了n,x,y,p,q之间的关系,特别是n=y/x,n=p*q这两个关系,乘积用于写更简易的for循环条件,而除法是为了写if语句的条件以及后面的执行语句。所以本道题目也应该按上述写出一个乘积和相除的关系,这样只要根据公式满足某些条件,即可达到题意。

"最大公约数和最小公倍数"的公式是根据一个数是由因子和另一个数的乘积,所以让这两个数相乘就可以得到某个公式了,再处理化简一下就可以了,而本道题目公式就是根据等差数列公式推导出来的,2S = n * (2k + n - 1)得出了一个乘积的公式,而n就是因子,同时也是分解出来的整数个数,这样就可以写条件了,只要条件成立,即可得到符合题意的值。

相关推荐
量子-Alex2 小时前
【大模型技术报告】Seed-Thinking-v1.5深度解读
人工智能·算法
Cher ~2 小时前
常见C++编译器套件
开发语言·c++
上海合宙LuatOS2 小时前
LuatOS ——Modbus RTU 通信模式
java·linux·服务器·开发语言·网络·嵌入式硬件·物联网
xyq20242 小时前
《jEasyUI 启用行内编辑》
开发语言
野生技术架构师2 小时前
Java 21虚拟线程 vs Kotlin协程:高并发编程模型的终极对决与选型思考
java·开发语言·kotlin
言之。2 小时前
Kotlin快速入门
android·开发语言·kotlin
Anastasiozzzz2 小时前
对抗大文件上传---分片加多重Hash判重
服务器·后端·算法·哈希算法
Vivienne_ChenW2 小时前
DDD领域模型在项目中的实战
java·开发语言·后端·设计模式
牙牙要健康2 小时前
【open3d】Windows 下编译 Open3D C++ 源码完整教程
开发语言·c++·windows