最大最小公倍数

【题目描述】
已知若干个正整数的和为S,求这若干个正整数的最小公倍数的最大值。

【输入】
第一行一个整数T,表示测试数据的组数。
接下来T行,每行包括一个正整数S,表示若干个正整数的和为S。

【输出】
输出T行,每行包括一个整数,表示和为S的若干个正整数的最小公倍数的最大值。

【样例输入】
2
4
7

【样例输出】
4
12

【数据范围限制】
40%的数据:S≤100;
80%的数据:S≤330,结果不会超过long long类型;
100%的数据:2≤S≤500,T≤10,结果不会超过25位整数。

【提示】
样例中第一组数据S=4,它能分解成S=1+1+1+1,S=1+1+2,S=1+3,S=2+2,S=4,很明显S=4时最小公倍数为4,是所有情况中最小公倍数最大的;第二组数据S=7,它能分解成S=3+4,3和4的最小公倍数是12,也是所有情况中最小公倍数最大的。

【题解】
这道题真是 害人不浅 啊!题目描述那么难懂就算了,还来个高精度!
我比赛时看到题目,哇塞!好难啊!我一直想着怎样把S拆成几个数,并没有找规律(因为最近经常做暴力水法就能过的题)。最后决定使用递归枚举 拆分S的所有情况,再找出最小值更新答案。
结果果然不出我所料,时间超限30!
30分的做法我就不细讲了,毕竟那个并不是正解。
这道题目其实是一道DP!我们可以把S拆分成这个样子:
\[{S=x_1^{y_1}+x_2^{y_2}+x_3^{y_3}+……x_i^{y_i}}\]
其中,\(x_1,x_2,x_3\cdots,x_i\)均为质数。
那么答案就等于 \({x_1^{y_1}\cdot x2^{y2}\cdot x_3^{y_3}\cdot\cdots\cdot x_i^{y_i}}\)(因为质数都是两两互质的,所以不用求它们的最大公因数)
为什么要拆成质数呢?我们可以设 \({A=x_1^{y_2}\cdot x_2^{y_2}}\),但是我们会发现,\({A>x_1^{y_1}+x_2^{y_2}}\),而 A 对答案的贡献却和 \({x_1^{y_1}\cdot x_2^{y_2}}\) 一样大。也就是说,合数不仅占空间,贡献还质数一样多,所以我们就只用质数,而不用合数了。

下面我们来设状态吧!
我们可以设\(f_i\)和为i的若干个质数的积的最大值 那么我们就可以写出状态转移方程了:\[f_i=max(f_i , f_{i-{x_j}^k} * {x_j}^k ) \quad(0\leq k\leq \frac{i}{x_j})\]
其中,j 枚举的是质数,k 枚举的是系数。
额……怎么这题竟然是一道DP?!

温馨提示:

  1. 我们可以先算出2~500的所有质数,然后DP,最后读入s(只要立刻输出f[s]就可以了)
  2. DP过程是三重循环的,最外层是j,枚举质数;然后是i,枚举1~500的所有数;最后是k,枚举系数。
  3. 记得打高精度!其实我们并不需要打出高精度 加、减、乘、除的所有运算,只要打 高精度乘低精度的运算高精度数的判断操作 即可!
  4. DP前记得把f数组直接初始化为1(不然答案都会是0哟)
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
struct node
{
    int len,a[120];
    node()
    {
        memset(a,0,sizeof(a));
        len=1;
    }
}f[510];
int s[500];
node chengfa_x(node n1,int x)//这里是高精度乘低精度!
{
    node no;int i;
    no.len=n1.len;
    for(i=1;i<=no.len;i++) no.a[i]=n1.a[i]*x;
    for(i=1;i<=no.len;i++)
    {
        no.a[i+1]+=no.a[i]/10;
        no.a[i]%=10;
    }
    i=no.len;
    while(no.a[i+1]>0)
    {
        i++;
        no.a[i+1]=no.a[i]/10;
        no.a[i]%=10;
    }
    while((no.a[i]==0)&&(i>1)) i--;
    no.len=i;
    return no;
}
bool pd(int k)//判断素数
{
    int tt=sqrt(k),i;
    for(i=2;i<=tt;i++)
    {
        if(k%i==0) return false;
    }
    return true;
}
node max(node x,node y)//高精度取max值
{
    if(x.len>y.len) return x;
    if(x.len<y.len) return y;
    for(int i=x.len;i>=1;i--)
    {
        if(x.a[i]>y.a[i]) return x;
        else if(x.a[i]<y.a[i]) return y;
    }
    return y;
}
int main()
{
    int t,i,j,c,sum=0,k,p;
    node T;
    scanf("%d",&t);
    for(i=2;i<=500;i++) if(pd(i))
    {
        s[++sum]=i;
    }
    for(i=0;i<=500;i++) f[i].a[1]=1;//初始化
    for(j=sum;j>0;j--)//DP过程
    {
        for(i=500;i>=1;i--)
        {
            p=1;
            for(k=1;k<=8;k++)
            {
                p=p*s[j];
                if(p>i) break;
                else
                {
                    f[i]=max(f[i],chengfa_x(f[i-p],p));
                }
            }
        }
    }
    while(t--)
    {
        scanf("%d",&c);
        for(i=f[c].len;i>=1;i--) printf("%d",f[c].a[i]);
        printf("\n");//输入一个,输出一个
    }
    return 0;
}

转载于:https://www.cnblogs.com/huangzihaoal/p/11154006.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值