题意:
给你一个数n,求1到n中因子最多的那个数,如果有多个这样因子相同的数,取数字最小的那个。
做法:
这个题首先要有前置技能,反素数。
我们通过打表每个数的因子个数可以发现:
数因子的数量在来回摆动,有波峰也有波谷,
而对于任意i<x,都有cnt[i]<cnt[x]的话,那么就把x叫做反素数。(cnt为因子个数)
反素数有两个性质:
no1: 一个反素数的质因子必然是从2开始的连续若干质数。
no2:x=2^a *3^b *5^c*7^d...(分解定理qwq,但是还有一个重要的限制,a>=b>=c>=d...)
有了前置技能后,我们考虑怎样写出来。
通过打表,我们可以发现2*3*5*7...*53(共16个)的乘积是大于1e18的,所以我们考虑16层循环太暴力了。
我们可以试试dfs
dfs参数首先有一个深度depth,控制到第几个数了,然后是precnt控制后面枚举的数的次数不超过前面的,这个可以大大的剪枝,
然后是num,说明这个数是多少,以及cnt这个数有多少因子。
其实这是我遇到的第一个把多重循环改成dfs的题,我的理解就是把dfs里的函数当作 for的一层来写,然后控制深度,其他并没有太大的差别。不过要注意传递的参数的选取,或者要不要考虑回溯。
代码如下:
#include<cstdio>
int p[16]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53};
const long long INF=1e18+7;
long long n;
long long res,miniNum;
void dfs(int depth,int precnt,long long num,long long cnt)
{
if(cnt>res)
{
res=cnt;
miniNum=num;
}
if(res==cnt&&num<miniNum)miniNum=num;
if(depth==16)return;
for(int i=1;i<64&&i<=precnt;++i)
{
if(num<=n/p[depth])
{
num*=p[depth];
// printf("depth+1=%d,num=%lld,cnt*(i+1)=%lld\n",depth+1,num,cnt*(i+1));
dfs(depth+1,i,num,cnt*(i+1));
}
}
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%lld",&n);
res=0,miniNum=INF;
dfs(0,100,1,1);
printf("%lld %lld\n",miniNum,res);
}
}