最小公倍数的最大问题

有一个数x, 0<=x<=1000, 把它拆分成几个数的和,x1+x2+......xn=x,使得x1,x2,x3.....xn的最小公倍数最大,
要求输入这个数 x,输出最小公倍数 S。 

题目来源:

http://community.csdn.net/Expert/TopicView.asp?id=5086754

http://bbs.xilu.com/cgi-bin/bbs/view?forum=rd&message=3761

可以通过使用动态规划来计算

首先容易证明,存在一种达到最大值的拆分方案,使得每个xi最多只有一个素因子(也就是说xi必然是1,素数或素数的幂),而且不同的xi之间互素(它们的素因子不同)。

然后就可以使用动态规划来计算。

中第k步是计算出x1,x2,...,xn使得
x1+x2+...+xn = x
x1,x2,...,xn的所有素因子都是前k个素数, (p1=2,p2=3,p3=5,...,pk)
x1,x2,...,xn的最小公倍数最大时的情况.
所以我每步添加一个新数(使用x+y的情况),必然有这两个数互素( (x,y)=1 )

为了简单起见,程序中用了gmp

#include <gmp.h>
#include <stdio.h>

#define MAX 10000
mpz_t mul[2][MAX+1];
mpz_t *muls1;
mpz_t *muls2;
int prime_list[MAX+1];
int prime_count;
mpz_t t;

void init_prime(){
    int i,j;
    prime_list[0]=prime_list[1]=1;
    for(i=2;i<=MAX;i++){
if(!prime_list[i]){//if prime
    for(j=i*i;j<=MAX;j+=i){
prime_list[j]=1;
    }
}
    }
    for(i=0;i<=MAX;i++){
if(!prime_list[i]){
    prime_list[prime_count++]=i;
}
    }
}

int main(){
    int i,j;
    for(i=0;i<=MAX;i++)
    {
        mpz_init(mul[0][i]);
mpz_init(mul[1][i]);
        mpz_set_ui(mul[0][i],0);
    }
    mpz_set_ui(mul[0][0],1);
    muls1 = mul[0];
    muls2 = mul[1];
    init_prime();
    mpz_init(t);

    for(i=0;i<prime_count;i++){
        int p = prime_list[i];
for(j=0;j<=MAX;j++){
    mpz_set(muls2[j],muls1[j]);
}
while(p<=MAX){
    for(j=0;j<=MAX-p;j++){
if(mpz_cmp_ui(muls2[j],0)==0)
    continue;
mpz_mul_ui(t, muls2[j],p);
if(mpz_cmp(t, muls1[j+p])>0){
    mpz_set(muls1[j+p],t);
}
    }
    p*=prime_list[i];
}
    }

    mpz_set_ui(muls1[1],1);

    for(i=1;i<=MAX;i++){
if(mpz_cmp(muls1[i-1],muls1[i])>0){
    mpz_set(muls1[i],muls1[i-1]);
}
    }

    for(i=2;i<=MAX;i++){
if(mpz_cmp(muls1[i-1],muls1[i])==0){
    mpz_set_ui(muls1[i],0);
}
    }
   
    for(i=1;i<=MAX;i++){
       int c;
       if(mpz_cmp_ui(muls1[i],0)==0)
    continue;
       printf("%d, ",i);
       mpz_out_str(stdout,10,muls1[i]);
       printf(" { ");
       for(j=0;j<prime_count;j++){
          c=0;
  if(mpz_cmp_ui(muls1[i],1)<=0)break;
          mpz_gcd_ui(t,muls1[i],prime_list[j]);
          while(mpz_cmp_ui(t,1)>0){
             mpz_cdiv_qr_ui(muls1[i],t,muls1[i],prime_list[j]);
             c++;
             mpz_gcd_ui(t,muls1[i],prime_list[j]);
          }
          if(c==1){
             printf("%d ",prime_list[j]);
          }else if(c>1){
             printf("%d^%d ",prime_list[j],c);
          }
       }
       printf("}/n");
    }
    return 0;
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值