题意:给你两个数n和m,让你将n切割成多个数字,求如何切割使得得到的数字的最小公倍数最大,最后输出最大公倍数对m取模后的结果。
题解:
首先,最小公倍数的求解:lcm(a,b)=a*b/gcd(a,b) 所以,要使最小公倍数lcm最大,则必须要让最大公约数gcd最小。而当两个数互质时,其最大公约数为1,此时满足题目所需要求。所以可以将题目转换成将n分割成若干个互质的数,并使这若干个质数乘积最大。
其次,互质的情况有一下几种:(1)1与任何自然数互质;(2)两个不同的质数互质;(3)一个合数与一个质数,当这两个数不是倍数关系时互质;(4)不含相同质因数的两个合数互质。
然后,根据上面的条件,知道现在需要知道小于等于n的质数有哪些,由于题目中n<=3000,不算太大,所以可以用素数筛将小于等于3000的素数存到一个数组中,然后在数组中找满足条件的素数进行后续操作(我用的是优化后的线性素数筛),且选择的素数可以是同一个素数选取多次,与完全背包类似。
其中,n相当于背包的总容量,而小于等于n的素数相当于待选的物品,每个物品的重量和价值就是这个数本身的大小,递推公式为:dp[i]=max(dp[i],dp[i-p]*p) (其中p为选择的素数大小,dp数组存的是i分解之后的数的最小公倍数),但是由于lcm的结果过大会溢出,同时,题目中还要求将结果对m取模,但如果在递推dp的过程中取模,在比较选择最优结果时就会出错,例如模数为m,上一次求出dp[k]=m+1,结果取模得1,下一次随便一个大于1得答案就会覆盖掉正确答案。但是不取模就会溢出,肯定也会造成答案错误。所以参照已有的题解,需要取对数来存储答案和比较最优结果,然后再新建一个新数组ans来存储取模后的结果。而dp数组就变成double类型,同时递推公式变为:dp[i]=max(dp[i],dp[i-p]+q*p) (其中,q为素数p为选取的次数,然后在取对数后乘积变成加法,即原本是dp[j-p]*(p^q),取对数后变成dp[i-p]+q*p),而存结果的数组:ans[i]=max(ans[i],(ans[i-p]*p)%m)
AC代码:
//HDU 3092
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <cstring>
using namespace std;
int N,M,prime[3010],cnt=0;//cnt标记1...3000的素数个数;prime[]存素数
bool isprime[3010];//标记是否为素数的数组
double dp[3010];//取对数之后的结果
int ans[3010];//存求模后的结果
void init()//优化后的线性素数筛,查找并存储3000以内的素数
{
memset(isprime,1,sizeof(isprime));//初始默认所有的数都是素数
isprime[0]=isprime[1]=0;//0和1不是素数
for(int i=2;i<=3000;i++)//对查找2...3000之间的素数
{
if(isprime[i])
prime[cnt++]=i;//保存素数
for(int j=0;j<cnt&&prime[j]*i<=3000;j++)//j为素数数组中的下标,遍历0...cnt;i为素数要乘的倍数
isprime[prime[j]*i]=0;//筛掉小于等于i的素数和由i的乘积构成的素数
}
}
int main()
{
init();//得到素数数组
while(~scanf("%d%d",&N,&M))
{
memset(dp,0,sizeof(dp));//数组初始化
for(int i=0;i<=N;i++)
ans[i]=1;//数组初始化
for(int i=0;i<cnt&&prime[i]<=N;i++)//在素数数组中开始遍历查找,且不能超过N
{
double tmp=log(prime[i]*1.0);//log的参数是double类型,暂存素数取对数后的结果值
for(int j=N;j>=prime[i];j--)//因为要找最大的最小公倍数,所以质数从大的开始找
for(int q=1,p=prime[i];p<=j;p*=prime[i],q++)//q记录素数prime[i]被选取的次数,即p中prime[i]的幂的次数,便于对dp数组更新的操作
if(dp[j]<dp[j-p]+q*tmp)
{
dp[j]=dp[j-p]+q*tmp;
ans[j]=(ans[j-p]*p)%M;
}
}
printf("%d\n",ans[N]);
}
return 0;
}
参考:
Least common multiple HDU - 3092题解
hdu 3092 Least common multiple(完全背包+数论)