acwing725. 完全数/质数下的约数问题及开根号改进(是否+1的思考)

题目介绍

在这里插入图片描述

直观枚举

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;

int main() {
    int num_cases; // 测试用例数量
    cin >> num_cases;

    for (int t = 0; t < num_cases; ++t) {
        int num; // 输入的整数
        cin >> num;
        int sum_divisors = 0; // 约数之和

        // 计算真约数之和
        for (int divisor = 1; divisor < num / 2 + 1; ++divisor) {
            if (num % divisor == 0) {
                sum_divisors += divisor;
            }
        }

        if (sum_divisors == num) {
            cout << num << " is perfect" << endl; // 完美数
        } else {
            cout << num << " is not perfect" << endl; // 非完美数
        }
    }

    return 0;
}

题目是除本身之外的约数,故这是最直接的枚举。这里要指出的是为什么我们只考虑到原数除以2呢?**是因为一个正整数的约数是不可能大于它的一半的(除了它本身,题目这里略过了)。**因为如果他是偶数那么二是他的约数,就刚好等于一半。如果是奇数,最大不可能有一半。这里附上chatgpt的证明。(+1是为了6这种一半也是约数的情况。)
chatgpt的证明
但是无情超时,原因在于1sC++至多处理1亿次,而题目范围双层循环外100次*内部给的范围是1亿。实际上超出了100倍,而这样的结论只能少判断一半,仍然超过50倍。

开根号改进

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;

int main() {
    int test_cases; // 测试用例数量
    cin >> test_cases;

    for (int t = 0; t < test_cases; ++t) {
        int num; // 输入的整数
        cin >> num;
        int sum_divisors = 0; // 约数之和
        for (int i = 1; i < sqrt(num); ++i) {
            if (num % i == 0) {
                sum_divisors += i;
                sum_divisors += num / i;
            }
        }
        sum_divisors -= num; // 减去自身

        if (sum_divisors == num) {
            cout << num << " is perfect" << endl; // 完美数
        } else {
            cout << num << " is not perfect" << endl; // 非完美数
        }
    }

    return 0;
}

依旧上传chatgpt的证明在这里插入图片描述
然而在这里我犯了一个错误,也是我要写这篇题记的根源。这便是我下意识写成了在i < sqrt(num)+1,这个+1的习惯帮助了我很多次,但是这一次想当然了。

  • 对于许多数来说,+1的写法也问题不大,只是多判了一次循环,也不影响时间复杂度。
  • 然而对此题来说,当输入1时,不加1都不会进入循环,+1却多循环一次,从而判错;当输入6时,开根号小于3,+1变成4,由于3也恰好是约数,又重复计算了一遍。

总结

1.约数的问题一律开根号,复杂度从百亿降到万。
2.对于循环是否+1的问题,需要具体问题具体分析。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天真且kk

觉得有帮助可以意思一下哈:)

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值