1. 程式人生 > >Humble Numbers(醜數) 超詳解!

Humble Numbers(醜數) 超詳解!

then 每一個 %d sample 如果 nbsp 輸出 define clu

給定一個素數集合 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\n",a[n]);//要誰找誰。
28     }
29     return 0;
30 }
技術分享



Humble Numbers(醜數) 超詳解!