题目链接: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;
}