dp-hdu-3092-Least common multiple

题目链接:

http://acm.hdu.edu.cn/showproblem.php?pid=3092

题目大意:

给一个数n,求一个划分a1,a2,...ak 使得ans=lcm(a1,a2,..,ak)最大,输出ans%m。

解题思路:

对于一个划分肯定是任意的i,j gcd(ai,aj)=1最优。如果gcd(ai,aj)>1 则最小公倍数肯定小些。

预处理所有的质数,转为话一个完全背包问题。

由于lcm很大,所以取对数处理。

代码:

#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<stack>
#include<list>
#include<queue>
#define eps 1e-6
#define INF 0x3fffffff
#define PI acos(-1.0)
#define ll __int64
#define lson l,m,(rt<<1)
#define rson m+1,r,(rt<<1)|1
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;


#define Maxn 3000 //三千内的质数430个

double dp[Maxn+10]; //取对数保证最小公倍数不会溢出
bool tmp[Maxn+10];
int pp[500],ans[Maxn+10];

int n,m,cnt;

void init()
{
    cnt=0;
    memset(tmp,false,sizeof(tmp));
    for(int i=2;i<=Maxn;i++) //素数晒选法
    {
        if(!tmp[i])
        {
            pp[++cnt]=i;
            for(int j=i*2;j<=Maxn;j+=i)
                tmp[j]=true;
        }
    }
    return ;
}

void solve()
{
    memset(dp,0,sizeof(dp));
    for(int i=0;i<=n;i++)
        ans[i]=1;
    for(int i=1;i<=cnt&&pp[i]<=n;i++)
    {
        double tt=log10(pp[i]*1.0);
        for(int j=n;j>=pp[i];j--) //相同质数应做为一个整体考虑
        {
            for(int k=pp[i],num=1;k<=j;k=k*pp[i],num++)
                if(dp[j-k]+tt*num>dp[j])
                {
                    dp[j]=dp[j-k]+tt*num;
                    ans[j]=(ans[j-k]*k)%m;
                }
        }
    }
}

int main()
{
    init();
    //printf("%d\n",cnt);
    while(~scanf("%d%d",&n,&m))
    {
        solve();
        printf("%d\n",ans[n]);
    }
    return 0;
}

你可能感兴趣的:(dp-hdu-3092-Least common multiple)