OJ测评超时【Time Limit Error】平方数相关问题优化样例

 

我们刷题时提交代码看到【Time Limit Error】都多多少少有点忧愁。


写这篇文章的起因是,我前几天刷题刷到了两道都是关于平方数的题目,第一道是浙江理工大学12月5号举办的线上新生赛的B题,第二道是国外网站Codeforces12月20号举办的一场比赛也是B题,并且都是如果不进行算法优化,一定会超时。这就不得不让我做一个总结。题目并不是很难,欢迎大家一起交流~

#include<stdio.h>
#include<math.h>
int count(int n, int arr[])
{
	int i = 0, count = 0;
	for (int i = 1; i <= 320;i++)
	{
		if (n%arr[i] == 0)
			count++;
//这个地方就实现了由判断一个数的因子是否为平方数转化为判断所涉及范围内所有平方数是否是其因子,
//避免了找因子的繁琐步骤,大大减少了运行时间。
//在算法竞赛中,可以解决平方数相关问题因参数太大而产生的超时问题。
		if (arr[i] > n)
			break;
	}
	return count;
}
int is_prime(int n)
{
	int i = 0;
	for (i = 2; i < n; i++)
	{
		if (n%i == 0)
			return 0;
//返回0,为非素数
	}
	return 1;
//返回1,为素数。
}
int main()
{
	int n = 0;
	scanf("%d", &n);
	int arr[321];
//存平方数。因为题目n最大值为100000,所以只需要从1存到100000范围内的平方数即可,
//100000开方等于316.227766...,所以我们至少要存316个平方数。
//为了避免越界访问,不妨把数组开到321,存下320个平方数。
	for (int i = 1; i <= 320; i++)
	{
			arr[i] = i*i;
	}
	int sum = 0;
	for (int i = 1; i <= n; i++)
//依据题意逐一统计1~n中的每一个数有多少因子是平方数,即有多少平方因子。
	{
		if (is_prime(i))
			sum++;
//为了进一步节省时间,我们可以先把过滤掉素数,因为素数的平方因子都仅有一个。
		else
			sum += count(i,arr);
//这里用一个函数统计一个数有多少平方因子。
	}
	printf("%d", sum);
	return 0;
}

 

国外的刷题网站显示的都是英文,我一般用网易有道词典翻译,这里附上译文:

平方数和立方数
时间限制每个测试1秒。
内存限制每个测试256MB。
Polycarp喜欢正整数的平方和立方。这是他喜欢的数字序列的开头:1、4、8、9,....
对于给定的数字n,数数Polycarp喜欢的从1到n的整数个数。
换句话说,找出这样的x的个数,x是一个正整数的平方或一个正整数的立方(或同时是一个平方和一个立方)。
输入
第一行包含一个整数t(1≤t≤20) - 测试用例的数量。
然后t行包含测试用例,每行一个。每一行包含一个整数n(1≤n≤10^9)。
输出
对于每个测试用例,输出您想要的答案—Polycarp喜欢的从1到n的整数的数目。

#include<stdio.h>
#include<string.h>
#include<math.h>
int main()
{
	int t;
	scanf("%d", &t);
	int arr1[32002];
	for (int i = 1; i <= 32000; i++)
	{
		arr1[i] = i*i;
	}
	//存平方数。因为题目n最大值为1000000000,所以只需要从1存到1000000000范围内的平方数即可,
	//1000000000开方等于31622.7766...,所以我们至少要存31622个平方数。
	//为了避免越界访问,不妨把数组开到32002,存下32000个平方数。
	int arr2[1002];
	for (int j = 1; j <= 1000; j++)
	{
		arr2[j] = j*j*j;
	}
	//存立方数。因为题目n最大值为1000000000,所以只需要从1存到1000000000范围内的立方数即可,
	//1000000000开立方等于1000,所以我们至少要存1000个平方数。
	//为了避免越界访问,不妨把数组开到1002,存下1000个平方数。
	int arr3[1002];
	int x = 1;
	for (int j = 1; j <= 1000; j++)
	{
		int q = pow(arr2[j], 1.0 / 2);
		if (q*q == arr2[j])
		{
			arr3[x] = j*j*j;
			x++;
		}
	}
	//存即是平方数又是立方数,由于立方数最大就是只是1000的立方,所以从立方数中找平方数更加省时。
	while (t--)
	{
		int n = 0;
		int flag1 = 0;
		scanf("%d", &n);
		if (n == 1000000000)
		{
			printf("%d\n", (int)pow(pow(10, 9), 1.0 / 2) + 1000 - x + 1);
			continue;
		}
        //后面我们只需要取比n小的所有平方数,加比n小的所有立方数,再减去即是平方数又是立方数以避免重复。
		for (int i = 1; i <= 32000; i++)
		{
			if (arr1[i] > n)
			{
				flag1 = i - 1;
				break;
			}
		}
		int flag2 = 0;
		for (int i = 1; i <= 1000; i++)
		{
			if (arr2[i] > n)
			{
				flag2 = i - 1;
				break;
			}
		}
		int flag3 = 0;
		for (int i = 1; i <= 1000; i++)
		{
			if (arr3[i] > n)
			{
				flag3 = i - 1;
				break;
			}
			if (i == 1000)
			{
				flag3 = x - 1;
			}
			//因为前面并没有判断即是平方数又是立方数的最大值,所以考虑n可能会大于arr3中所有的数。
			//判断i=1000说明没有符合arr3[i]>n条件的,所以1~n包括了所有的arr3中的数,前面我们也提前用x统计了arr3中的个数。
		}
		printf("%d\n", flag1 + flag2-flag3);
		//最后输出1~n中是平方数和立方数的个数。
	}
	return 0;
}

 

提交后,显示第一题实际运行时间196ms,第二题实际运行时间15ms,可以满足题目要求。

猜你喜欢

转载自blog.csdn.net/phantomthief1412/article/details/122072952