案例十三:既是平方数也是立方数

分析:

本题可以采用枚举求解。但不同的枚举思路效率不一样,程序运行所需的时间也不一样
方法一:枚举1~n的每个数k,判断k是否为某个数的平方,且是另一个数的立方。方法是,求出k的平方根,取整,设为x,并求出k的立方根,取整,设为y,判断k==x*x and k==y*y*y是否成立。但这里有一个风险,浮点数在计算机里无法精确表示。因此统计出来的个数可能不准确。更保险的做法是用:x=sqrt(k)+0.00005、y=cbrt(k)+0.00005
这种方法虽然正确,但效率很低。假设计算机每秒钟能执行1亿次(100000000)运算,那么这种方法只能通过50%数据的评测,甚至是40%数据的评测,因为求平方根、立方根的运算比较复杂。其他数据的评测结果是超时 

#include<iostream>
#include<cmath>
using namespace std;
int main()
{
    long long n; cin >> n;
    long long x, y, cnt = 0; //计数器
    for(long long k=1;k<=n;k++){
        x = sqrt(k) + 0.00005; y = cbrt(k) + 0.00005;
        if(k==x*x and k==y*y*y) cnt++;
    }
    cout << cnt << endl;
    return 0;
}

方法二:不是直接枚举1~n的每个数k,而是枚举每个立方根y,显然y的取值最大是m=n的立方根,在程序中应该表示成cbrt(n)+0.00005,对1~m范围内的每个数y,求x=sqrt(y*y*y)+0.00005,判断x*x==y*y*y是否成立
当n取到最大为9223372036854775807时,只需要循环2097151次,因此可以通过所有数据的评测

#include<iostream>
#include<cmath>
{
    long long n; cin >> n;
    long long x, y, cnt = 0;
    int m = cbrt(n) + 0.00005
    for(y=1;y<=m;y++){
        x = sqrt(y*y*y) + 0.00005;
        if(x*x==y*y*y) cnt++;
    }
    cout << cnt << endl;
    return 0;
}

方法三:如果一个数既是平方数,又是立方数,则它一定是某个数的6次方。以64为例,64=2^6,因此,它等于8^2、也等于4^3。因此,对输入的n,可以从1开始枚举每个自然数k,只要k*k*kk*k*k<=n,则k*k*k*k*k*k=k既是平方数、又是立方数。但是,在本题中,n和k必须定义成无符号long long型,即unsigned long long。这是因为当n取到本题的最大值9223372036854775807时,k取1448,k^6<n;然后k递增1,变成1449,在数学上1449^6已经超过n,但是如果k和n定义成long long型,当k取到1449时,k^6是一个负数,从而会继续循环,事实上,这时会陷入死循环

#include<iostream>
using namespace std;
int main()
{
    unsigned long long n, k; cin >> n;
    int cnt = 0;
    for(k = 1; k*k*k*k*k*k<=n; k++)
        cnt++;
    cout << cnt << endl;
    return 0; 
}
    

方法四:1~n到底有多少个数符合本题要求呢?答案其实就是n的6次平方根向下取整
例如,n=10000时,10000的6次平方根=4.6415888.=4,因此1^6、2^6、3^6、4^6都是小于10000,而5^6>10000
但是,用pow函数求n的6次平方根向下取整时,由于精度丢失,无法求得准确的结果,例如n=1000000时,n本来应该是10,但用pow函数求得的结果为9.999999999996,再取整就得到9,这个答案是错的,因此需要加上一个很小的浮点数(如0.0005),注意,不能加上一个较大的浮点数,如0.5,否则像n=10000,pow(n,1.0/6)+0.5的值超过了5.0,再取整就得到5,这个答案是错的

#include<iostream>
#include<cmath>
using namespace std;
int main()
{
    long long n; cin >> n;
    int cnt = pow(n, 1.0/6)+0.0005
    cout << cnt << endl;
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值