2020ccpc 威海 Clock Master(多个数的lcm+分组背包)

68 篇文章 1 订阅
13 篇文章 0 订阅

题目大意:给出一个数字 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

https://blog.csdn.net/qq_45458915/article/details/109285171?utm_medium=distribute.pc_relevant.none-task-blog-title-1&spm=1001.2101.3001.4242
 

#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;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值