传智杯(省赛第二场) 小苯的好数 (经典题)

系列文章目录

  传智杯,蓝桥杯基础题类型(如何降低时间复杂度)


文章目录


前言

   在这场复赛中,我感觉这题是最有价值的,因为它能让我想起来原来C语言求素数的类似思想,而且这道题如果暴力求的话 虽然可以通过部分测试用例,但是在数组足够大的范围的时候,我们就会发现运行时间是过不去的,(我们在考虑一道算法的时间复杂度的时候就要按最差情况来看待)所以我们就要采取一些措施来降低这个时间复杂度,具体应该如何降低时间复杂度呢,请开始我们今天的内容


题目参考:

题目输入输出样例:

一、核心思想:

        这道题就是一个看起来挺简单,但是做起来还是要仔细的来分析条件的,就比如说我在找给的数组中的数的因子的时候,我就没有养成习惯,还是傻傻的一个一个找,但是这样的暴力遍历就会造成高复杂度,而我们可以通过利用因子成对的对称性来降低时间复杂度,这点在当时判断一个数是否是素数时也有体现,下面我会对其进行总结。而且这道题如果我们按着题干分析的话判断的条件也是不一样的,这道题我们要找的好数的定义应该是偶数或者如果是奇数的话我们要判断是否是完全平方数。


二、本题的具体实现:

      其实这题并没有那么难,只不过我感觉有价值的是我们如何一步一步通过自己的思维来降低算法的时间复杂度,这是我感觉最重要的。

      下面是具体实现

强调和解释的点:

 1.找因子       

就是我们首先要求这个定义函数的因子有哪些,我们可以采用从1开始一个一个开始找,也可以用因子的对称性法则来降低时间复杂度,这里给大家提供两种思路:

这是来判断一个数是否是完全平方数的

1.直接用Math类的sqrt()方法:

 private static boolean isPerfectSquare(int x) {
        long sqrt = (long) Math.sqrt(x);
        return sqrt * sqrt == x;
    }

2.可以通过二分查找来确定,这个二分思想在各种地方应用都非常多

public static boolean isPerfectSquare(int num) {
        if (num < 0) {
            return false; // 负数不可能是完全平方数
        }
        
        int left = 0, right = num;
        
        while (left <= right) {
            int mid = left + (right - left) / 2;
            long square = (long) mid * mid;
            
            if (square == num) {
                return true; // 找到完全平方数
            } else if (square < num) {
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }
        
        return false; // 没有找到完全平方数
    }

2.利用前缀和

适用场景:频繁查询数组的区间统计(如总和、计数等)。

   

int[] prefix = new int[n + 1];
for (int i = 0; i < n; i++) {
    prefix[i + 1] = prefix[i] + (isGood[i] ? 1 : 0);
}

前缀和的核心思想:通过预处理将区间查询的复杂度从 O(n) 优化到 O(1)

三、完整代码实现:

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int T = in.nextInt();
        while (T-- > 0) {
            int n = in.nextInt();
            int q = in.nextInt();
            int[] prefix = new int[n + 1]; // 前缀和数组
            for (int i = 0; i < n; i++) {
                int x = in.nextInt();
                boolean isGood = (x % 2 == 0) || isPerfectSquare(x); // 关键判断
                prefix[i + 1] = prefix[i] + (isGood ? 1 : 0);
            }
            while (q-- > 0) {
                int l = in.nextInt();
                int r = in.nextInt();
                System.out.println(prefix[r] - prefix[l - 1]); // O(1)查询
            }
        }
    }

    // 判断x是否是完全平方数
    private static boolean isPerfectSquare(int x) {
        long sqrt = (long) Math.sqrt(x);
        return sqrt * sqrt == x;
    }
}

总结

以上就是我要讲的内容,其实我最想讲的点还是因子对称性的使用场景,因为这可以显著时间复杂度。

通过利用因子成对的对称性,将遍历范围从 O(x) 优化到 O(x​),是数论问题中降低时间复杂度的核心技巧。这种方法适用于所有需要寻找因子或判断素数的问题,如:

  • 计算因子和(题目中的优化)
  • 判断素数
  • 分解质因数
  • 寻找完全平方数等。

下面会开始更新前缀和的应用和Java的一些类的实现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值