分析:
本题可以采用枚举求解。但不同的枚举思路效率不一样,程序运行所需的时间也不一样
方法一:枚举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;
}