zoukankan      html  css  js  c++  java
  • Humble Numbers(丑数) 超详解!

    给定一个素数集合 S = { p[1],p[2],...,p[k] },大于 1 且素因子都属于 S 的数我们成为丑数(Humble Numbers or Ugly Numbers),记第 n 大的丑数为 h[n]。

    算法 1:

      一种最容易想到的方法当然就是从 2 开始一个一个的判断一个数是否为丑数。这种方法的复杂度约为 O( k * h[n]),铁定超时(如果你这样做而没有超时,请跟 tenshi 联系)  

    算法 2:

      看来只有一个一个地主动生成丑数了 :

      我最早做这题的时候,用的是一种比较烂的生成方法,复杂度为 O( k * n * log(n) )。

      算法流程如下:

    1.初始化最小堆,内置一个元素 1

    2.i=0 ,表示求第 i 个丑数(默认 h[0]=1)

    3.if i>n then goto 7

    4.取出堆中最小的元素 x(如果有多个最小元素,全部取出来), h[i]:= x,i:=i+1

    5.把 x*p[1] , x*p[2] ... ,x*p[n] 放入堆中

    6.goto 3

    7.结束

      这个算法要使用一个最小堆(Heap)的数据结构,。不会超时。

      

    算法 3:

      算法2虽然速度还可以,但是算法复杂度还是有点高。这里介绍一下 UsacoGate 提供的标准程序的算法。首先我们知道这样的东西:如果前 m-1 个丑数已经求出来了(包含 0),那么第 m 个数肯定是由前面某个丑数乘 S 里的素数得来的。假设是 h[pindex[i]] 乘 p[i] 而得到 h[m] 的话,把每次乘 p[i] 的 pindex[i] 列出来,肯定是单调的!利用这个,我们可以得到这样的算法:

    1.nhum=0 ,表示求第 nhum 个丑数(默认 h[0]=1)

    2.令 pindex[i]=0 ,表示一开始无论怎么乘,都是乘 h[0]

    3.if (nhum > n ) then goto 7

    4.分别求出 h[pindex[i]]*p[i]的值,找出比 h[nhum-1] 大的最小值

    5.把这个最小值 h[pindex[minp]]*p[minp] 存入 h[nhum]

    6.nhum:=nhum+1;

    7.结束

      这个算法复杂度显然为 O( n * k ),已经相当不错了

    以上是某个博客的做题心得吧!我把他引用过来仅仅为了介绍丑数的概念

    我就以一道例题介绍丑数吧!

    description

    只有质数2,3,5,7这几个作为因子的数叫做,丑数,比如前20个丑数是(从小到大来说) 1,2,3,4,6,7,8,9,10,12,14,15,16,18,20,21,24,25和27.
    							

    input

    我们给你个n(1<=m<=5842)当输入n为0结束。
    							

    output

    输出第n个丑数。每个数一行。
    							

    sample_input

    1
    2
    3
    4
    11
    
    							

    sample_output

    1
    2
    3
    4
    12
    
    
    
    
    关于丑数的含义在题目中已有解释,有的题目中忽略了“7”这个质因子,其实这都不是最重要的重要的是掌握其处理的方法。
    首先,判断一个数是否为丑数的方法如下:
    复制代码
     1 int find_uglynum(int a)
     2 {
     3     while(a%2==0)//将这个数中的质因子 2 耗尽
     4        a/=2;
     5     while(a%3==0) 
     6        a/=3;
     7     while(a%5==0)
     8        a/=5;
     9     while(a/7==0)
    10        a/=7;
    11     if(a==1)
    12        return 1;
    13     else 
    14        return 0;
    15 }
    复制代码
    但是这种方法过于费时,下面给出该题解题思路:
             首先,第一个丑数为“1”,后面的每一个丑数都是由前一个丑数乘2、3、5或7而来,那么后一个丑数就是前一个乘这四个数得到的最小值,for example:第一个:1,第二个:1*2、1*3、1*5或1*7,显然为2,第三个:2*2,1*3,1*5或1*7,显然是3,第四个:2*2,,2*3,1*5,1*7为4,第五个:3*2,2*3,1*5,1*7……   聪明的你是否看明白了呢?
    
    
    下面给出本题的代码:
    复制代码
     1 #include <iostream>
     2 #include <cstdio>
     3 using namespace std;
     4 #define min(a,b) ((a)<(b)?(a):(b))
     5 #define min4(a,b,c,d) min(min(a,b),min(c,d))
     6 int a[5850];
     7 int main()
     8 {
     9     //freopen("data.in","r",stdin);
    10     //freopen("data.out","w",stdout);
    11 
    12     int n=1;
    13 
    14     int p2,p3,p5,p7;
    15     p2=p3=p5=p7=1;
    16     a[1]=1;
    17     while(n<5843)//枚举5842个丑数,放在数组a里。
    18     {
    19         a[++n]=min4(2*a[p2],3*a[p3],5*a[p5],7*a[p7]);//从现在枚举的4个丑数里,先选择小的放在a里。
    20         if(a[n]==2*a[p2])p2++;//如果a[n]==2*a[p2],2*a[p2]可能是吧a[n]枚举出的数,这样p2++,也可能是重复的枚举,这样也是p2++,总之p2++。
    21         if(a[n]==3*a[p3])p3++;//同理。
    22         if(a[n]==5*a[p5])p5++;//同理。
    23         if(a[n]==7*a[p7])p7++;//同理。
    24     }
    25     while(scanf("%d",&n)&&n)
    26     {
    27         printf("%d
    ",a[n]);//要谁找谁。
    28     }
    29     return 0;
    30 }
    复制代码
    
    
    
    
    
    
    
  • 相关阅读:
    判断ImageIcon创建成功
    Node中的explorer views的双击事件
    Oracle数据类型
    Sql三种行转列
    数据库迁移
    并发采集同一站点被封的解决方案
    .net获取版本号的三种方法
    List转DataSet
    Orcale自增长主键
    python学习笔记数字和表达式
  • 原文地址:https://www.cnblogs.com/yechanglv/p/6941971.html
Copyright © 2011-2022 走看看