题目大意:给出一个数字 n ,要求分解成:a[ 0 ] + a[ 1 ] + ... + a[ m ] = n,( m 没有约束 ),使得 lcm( a[ 0 ] , a[ 1 ] , ... a[ m ] ) 最大,输出这个最大值的对数
思路:将n分解成m个数之后,问题就变成了如何求m个数的lcm。有一种做法是用gcd(a,b)*lcm(a,b)==a*b。但是将m个数进行质因数分解更为普遍。
即
那么m个数,每一个数都可以这样分解。
那么m个数的lcm就是每个质因子的最大cnti对应的乘积。
于是发现如果m个数字中,比如有一个存在3^1,有一个存在3^3,那么一定取更大的3^3作为lcm的贡献,而不是这个3^1,那么也就是说,这个3^1被浪费了。于是想lcm最大,即让浪费最小。
构造质数相加:p1^cnt1+p2^cnt2+.....pm^cntm==n;
题目所求即是p1^cnt1*p2^cnt2*p3^cnt3......pm^cntm;
由对数函数log(a*b)=log(a)+log(b)可转化成:
log(p1^cnt1)+log(p2^cnt2)+...log(pm^cntm);
那么此时就变成了分组背包问题。
有p1,p2,p3....pm个组。每个组里面的物品的体积分别是pi^1,pi^2....pi^cnti,且每一组只能选一个物品。同时每个物品的价值为log(pi^x)[价值最大为n,也就是Log(n),预处理即可]
于是预处理质数,进行分组背包,再滚动数组倒序枚举。
思路参考:
https://blog.csdn.net/qq_39641976/article/details/109299821
#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<cstdio>
#include<algorithm>
#define debug(a) cout<<#a<<"="<<a<<endl;
using namespace std;
const int maxn=3e4+100;
typedef long long LL;
double dp[maxn],w[maxn];///w[]是价值
LL primes[maxn],cnt=0;
bool vis[maxn];
void init()
{
for(LL i=2;i<maxn;i++){
if(!vis[i]) primes[++cnt]=i,vis[i]=true;
for(LL j=i;j<=maxn/i;j++){
vis[i*j]=true;
}
}
}
void solve()
{
for(LL i=1;i<maxn;i++){
w[i]=log(i);
}
for(LL i=1;i<=cnt;i++){///枚举物品组数
for(LL j=maxn-1;j>=primes[i];j--){///枚举背包容量
for(LL k=primes[i];k<=j;k*=primes[i]){///枚举是该组的第几个物品
if(j>=k){
dp[j]=max(dp[j],dp[j-k]+w[k]);
}
}
}
}
}
int main(void)
{
cin.tie(0);std::ios::sync_with_stdio(false);
init();
solve();
int t;cin>>t;
while(t--)
{
int n;cin>>n;
printf("%.10f\n",dp[n]);
}
return 0;
}