(算法竞赛进阶指南)反素数(约数)

题目链接:198. 反素数 - AcWing题库

题意:

对于任何正整数 x,其约数的个数记作 g(x),例如 g(1)=1、g(6)=4。

如果某个正整数 x 满足:对于任意的小于 x 的正整数 i,都有 g(x)>g(i),则称 x 为反素数。

例如,整数 1,2,4,6 等都是反素数。

现在给定一个数 N,请求出不超过 N 的最大的反素数。

分析:这道题考察的是算术基本定理,我们先来看一下反素数具备什么样的性质

通俗来讲,反素数就是1~N中约数个数最多且值最小的数,我们知道对于约数个数有贡献的不是质因子的值,而是质因子的指数,具体来说的话,假如有一个一次质因子p,那么p取2或者3对约数个数的影响的是一样的,而通过定义我们不难看出,反素数要尽可能的小,所以这就要求我们在选取质数的同时要尽可能地选小质数,这样才能选取更多的质数,那么问题来了,我们可供选择的质数范围是多大呢?

仔细想想,前11个质数的乘积2*3*5*7*11*13*17*19*23*29*31>2e9,所以我们求取的反素数含有的质因子是不可能大于29的,因为假如答案ans的质因子分解中有一个质因子p大于29,那么在1~29的质因子必然有一个不存在,不妨假设p‘不存在,那么我们将ans中的p换成p’,ans就会变成ans/p*p'<ans,显然ans/p*p'更适合作为反素数,所以这也就说明我们的答案含有的素数一定是从小到大连续的,且指数是从大到小的,因为指数决定了约数的个数,但是指数大的在小的质数上会使值总体更小,所以我们答案的形式就比较明确了

然后我们直接暴力搜索前10个质数的指数即可,剪枝策略就是每一个质数的指数都会小于前一个质数的指数,这就要求我们在搜索过程中必须记录前一个指数的值,否则会超时

下面是代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
using namespace std;
typedef long long ll;
const int N=30;
int prime[N],cnt;
ll n,ans,anscnt;
bool vis[N];
void init()
{
	for(int i=2;i<N;i++)
	{
		if(!vis[i]) prime[++cnt]=i;
		for(int j=1;i*prime[j]&&j<=cnt;j++)
		{
			vis[i*prime[j]]=true;
			if(i%prime[j]==0) break;
		}
	}
}
void dfs(int pos,int last,int t,ll tt)//t记录当前约数个数,tt记录当前值 
{
	if(tt>n||pos>cnt) return ;
	if(t>anscnt)
	{
		anscnt=t;
		ans=tt;
	}
	else if(t==anscnt)
		ans=min(ans,tt);
	for(int i=1,j=prime[pos];j*tt<=n&&i<=last;i++,j*=prime[pos])
		dfs(pos+1,i,t*(1+i),tt*j);
}
int main()
{
	init();
	cin>>n;
	ans=2e9,anscnt=0;
	dfs(1,30,1,1);
	printf("%lld",ans);
	return 0;
} 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值