您的位置:首页 > 其它

素因子去重(蓝桥杯练习题)

2019-01-30 18:22 148 查看

题目:
问题描述
  给定一个正整数n,求一个正整数p,满足p仅包含n的所有素因子,且每个素因子的次数不大于1
输入格式
  一个整数,表示n
输出格式
  输出一行,包含一个整数p。
样例输入
1000
样例输出
10
数据规模和约定
  n<=10^12
  样例解释:n=1000=2^353,p=2*5=10

应评论区的要求新增例题解释:
题意解释:
给定一个正整数,求该正整数所有素因子的乘积。每个素因子作为乘数仅一次。
举个栗子就是25,素因子有1,5;那我们要求的就是1*5 = 5。
再举个栗子,2212,2212 = 2 * 2 * 41 * 13;那么它包含的素因子就是2 , 13 ,41,要求的结果就是2 * 13 * 41 = 1106。
如果还有疑问欢迎在底部评论区提出。

提示:思路较长,想直接查看代码和答案请直接传送到页面最底下
思路:
先讲讲我的第一思路:定义一个数组,存放该正整数的所有素数因子(可以先求出所有因子再一个个判断,也可以先判断因子然后再判断该因子是否素数最后再存到数组里,前者可以定义个函数,后者直接循环+条件判断就好);到这有了一个思路上的分叉口:
**思路一:**我们可以每判断出来一个素数因子i,就n/=i;然后for循环重新从2开始判断因子。这样做的好处是肯定可以判断出所有的因子,尽管可能会出现2、2、3这样的重复,但是我们可以在后面进行去重(划重点,去重操作其实也挺有意思的,后面会提到我碰到的坑);去重后直接求去重后数组元素它们的乘积即可得出答案
**思路二:**我们可以不重置循环,而是从2开始判断是否是素数因子,比如对于正整数1000,先判断出2为素数因子,接下来继续从3判断,不对,4,不对,5,对,继续,这个时候由于i的范围是小于等于sqrt(n),故我们就可以直接让这两个素数因子相乘得到结果10。
这个思路姑且不论正确与否(在下面的代码中我会论证这个思路的正确性),但可以省略掉去重操作,相比思路一,这是一个进步。因为根据题意,我们不一定非要判断出来n满足n = x * x * y * z这样的素数因子表达式,只需要是n的素因子,就满足。
生物具有趋利避害的本能,故我们当然先从实现相对简单的思路二入手啦,代码如下:

#include<iostream>
#include<algorithm>
#include<cmath>
#define maxn 100005
#define ll long long
using namespace std;
ll n ,p = 1;
int a[maxn],flag;
int main(void)
{
cin >> n;
for(int i = 2; i < n; i++)
{
int OK = 1;
if(n % i == 0) //判断i是不是n的因子
{
for(int j = 2; j <= sqrt(i); j++) //如果是n的因子,则判断i是否为素数
{
if(i % j == 0)
{
OK = 0;
break;
}
}
if(OK) //如果是因子就存放到数组a中,flag是一个标识变量,用于推动a的下标
{
a[flag] = i;
flag += 1;
}
}
}
for(int i = 0; i < flag; i++)
{
p *= a[i];
}
cout << p;
return 0;
}

经过初步的样例检测(n=1000,p=10;n=533,p=533),代码符合要求,故我尝试性提交了一下,结果:

运行超时了!?what。。。
于是很郁闷的回过头看了一眼数据规模,n是1e+12的规模,也难怪会超时。。。(上述代码时间复杂度约为O(n^(3/2)))

那么就只有两个选择了:优化代码或者改用思路一。但这里很难再进行优化,外层循环的i变量必须要保证小于n而非n的平方根(因为可能有533=13*41这种数字的出现),而里面的循环变量j一开始就是小于n的平方根,所以这条路走不通了,改换思路一,改动循环:

for(int i = 2; i < n; i++)
{
int OK = 1;
if(n % i == 0) //判断i是不是n的因子
{
for(int j = 2; j <= sqrt(i); j++) //如果是n的因子,则判断i是否为素数
{
if(i % j == 0)
{
OK = 0;
break;
}
}
if(OK)
{
n /= i;
a[flag++] = i;
i = 2;
}
}
}

接下来要做的就是去重。去重操作也有三种操作选择:调用现成的unique函数、或者自己写一个函数来去除重复数,最笨的也是最好理解的当然就是直接定义第二个数组存放然后不断遍历保证不重复。具体讲解又分了三种思路,我另写了一篇来专门论述如何去重,在这就不再多加赘述了。我直接用了unique函数(在这里我遇到了一个坑:不太理解为什么unique返回的最后一个不重复数字的位置会需要再减去a,但这个问题后来得到了解决,详见我的另一篇专门讲如何去重的blog),这次没有超时,因为剪枝优化比较成功,贴AC代码如下。
AC代码:

#include<iostream>
#include<algorithm>
#include<cmath>
#define maxn 100005
#define ll long long
using namespace std;
ll n ,p = 1;
int a[maxn],flag;
int main(void)
{
cin >> n;
for(int i = 2; i <= n; i++)
{
int OK = 1;
if(n % i == 0) //判断i是不是n的因子
{
for(int j = 2; j <= sqrt(i); j++) //如果是n的因子,则判断i是否为素数
{
if(i % j == 0)
{
OK = 0;
break;
}
}
if(OK)
{
n /= i;
a[flag++] = i;
i = 2;
}
}
}
int flag2 = unique (a , a + flag) - a;
for(int i = 0; i < flag2; i++)
{
p *= a[i];
}
cout << p;
return 0;
}

另外,考虑到蓝桥杯的部分数据可能是VIP用户才可以看到的,我另外贴一下蓝桥杯的样例数据:
533 533
6021 669
1911 273
770518 770518
887244 443622
79561585 79561585
80189196 40094598
81095240 20273810
1822020238 1822020238
18374001125 734960045
顺便推荐下同道题目的另外一种思路,因为这道题目是昨天才在蓝桥杯官网上更新,所以网上的题解应该暂时还不太多,这位博主的思路在我看来应该是最优解之一了,传送门:https://blog.csdn.net/qq_40774175/article/details/86694788
最后,希望各位能对我的博文提出宝贵的意见!望不吝赐教

2019.02.04

最近两天写了一篇筛法求素数的blog之后发现之前的方法还是不够好,在这里直接提供那篇文章的地址,感兴趣的小伙伴可以去看看。
https://www.geek-share.com/detail/2760251455.html
当然,有时间的话我也会用新方法对题解进行优化,感谢各位支持~!

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: