C語言程序設計100例之(14):丑數


例14   丑數

問題描述

丑數是其質因子只可能是2,3或5的數。前10個丑數分別為1, 2, 3, 4, 5, 6, 8, 9, 10, 12。輸入一個正整數n,求第n個丑數。

輸入格式

每行為一個正整數n (n <= 1500),輸入n=0結束。

輸出格式

每行輸出一個整數,表示求得的第n個丑數。

輸入樣例

1

2

50

0

輸出樣例

1

2

243

         (1)編程思路。

        根據丑數的定義,丑數從小到大排列的序列中的一個數應該是其前面某個數乘以2、3或者5的結果。因此,可以定義一個數組num[1501]來順序保存序列中的丑數,數組里面的每一個元素的值是前面的某個元素值乘以2、3或者5得到。

        問題的關鍵是怎樣確保數組里面的各元素是按值的大小依次生成的。

        假設數組中已經有若干個序列中的元素,排好序后存在數組中。把序列中現有的最大的數記做M。由於序列中的下一個數肯定是前面某一個數乘以2、3或者5的結果。首先考慮把已有的每個數乘以2。在乘以2的時候,能得到若干個結果小於或等於M的。由於數組中的元素是按照順序生成的,小於或者等於M的數肯定已經在數組中了,不需再次考慮;還會得到若干個大於M的結果,但只需要第一個大於M的結果,因為數組中的元素是按值從小到大順序生成的,其他更大的結果可以以后再說,記下得到的第一個乘以2后大於M的數M2。同樣,把已有的每一個數乘以3和5,記下得到的第一個大於M的結果M3和M5。那么,序列中下一個數應該是M2、M3和M5三個數的最小者。

        事實上,上面所說的把數組中已有的每個數分別都乘以2、3和5,是不需要的,因為已有的數是按順序存在數組中的。對乘以2而言,肯定存在某一個數T2,排在它之前的每一個數乘以2得到的結果都會小於已有最大的數,在它之后的每一個數乘以2得到的結果都會太大。因此,只需要記下這個數的位置P2,同時每次生成一個新的序列中的數的時候,去更新這個P2。對乘以3和5而言,存在着同樣的P3和P5。

        定義變量index保存當前待生成的數在序列中的序號,顯然,已生成的序列中的最大元素為Num[curIndex-1]。

        定義3個指針變量int  *p2,*p3,*p5;分別指向數組中的3個元素,排在所指元素之前的每一個數乘以2(或3、或5)得到的結果都會小於已有最大的數num[index-1],在所指元素之后的每一個數乘以2(或3、或5)得到的結果都會太大。

        初始時,num[1] = 1、index =2、p2 = p3 = p5 = &num[1]。

        生成第index個元素的方法為:

         if (*p2 * 2<*p3 * 3)  min = *p2 * 2;

         else              min= *p3 * 3;

         if (min> *p5 * 5)    min=*p5 * 5;

         num[index] = min;

        第index個元素生成后,需要對指針p2、p3和p5進行更新,更新方法為:

        if(num[index]==*p2*2)   p2++;

        if(num[index]==*p3*3)   p3++;

        if(num[index]==*p5*5)   p5++;

        (2)源程序。

#include <stdio.h>

#include <string.h>

int main()

{

    int num[1501],index,min,n;

    int *p2,*p3,*p5;

    p2=p3=p5=&num[1];

    num[1]=1;

    for  (index=2;index<=1500;index++)

    {

         if (*p2 * 2<*p3 * 3) min = *p2 * 2;

        else  min= *p3 * 3;

        if (min> *p5 * 5) min=*p5 * 5;

        num[index] = min;

        if(num[index]==*p2*2) p2++;

        if(num[index]==*p3*3) p3++;

        if(num[index]==*p5*5) p5++;

    }

    while(scanf("%d",&n) && n!=0)

    {

         printf("%d\n",num[n]);

    }

    return 0;

}

習題14

14-1  Hamming Problem

        本題選自北大POJ 題庫 (http://poj.org/problem?id=2545)

Description

For each three prime numbers p1, p2 and p3, let's define Hamming sequence Hi(p1, p2, p3), i=1, ... as containing in increasing order all the natural numbers whose only prime divisors are p1, p2 or p3.

For example, H(2, 3, 5) = 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 16, 18, 20, 24, 25, 27, ...

So H5(2, 3, 5)=6.

Input

In the single line of input file there are space-separated integers p1 p2 p3 i.

Output

The output file must contain the single integer - Hi(p1, p2, p3). All numbers in input and output are less than 10^18.

Sample Input

7 13 19 100

Sample Output

26590291

        (1)編程思路。

        弄明白了例14的編程思想,本題就簡單了,無非是將例14中的質數2,3、5參數化為p1、p2和p3而已。

         (2)源程序。

#include <stdio.h>

int main()

{

         long long p1,p2,p3,H[100005];

         int n,i,a1,a2,a3;

         while(scanf("%lld%lld%lld%d",&p1,&p2,&p3,&n)!=EOF)

         {

                   H[0] = 1;

                   a1=a2=a3=0;

                   for (i = 1;i<=n;i++)

                   {

                            H[i] = p1*H[a1]<p2*H[a2]?p1*H[a1]:p2*H[a2];

                            if (H[i]>p3*H[a3]) H[i]=p3*H[a3];

                            if(H[i]==p1*H[a1])  a1++;

                            if(H[i]==p2*H[a2])  a2++;

                            if(H[i]==p3*H[a3])  a3++;

                   }

                   printf("%lld\n",H[n]);

         }

    return 0;

14-2  丑數 Humble Numbers

        本題選自洛谷題庫 (https://www.luogu.org/problem/ P2723)

題目描述

對於一給定的素數集合 S = {p1, p2, ..., pK},考慮一個正整數集合,該集合中任一元素的質因數全部屬於S。這個正整數集合包括,p1、p1*p2、p1*p1、p1*p2*p3...(還有其它)。該集合被稱為S集合的“丑數集合”。注意:我們認為1不是一個丑數。

將丑數集合中每個數從小到大排列,每個丑數都是素數集合中的數的乘積,第N個“丑數”就是在能由素數集合中的數相乘得來的(包括它本身)第n小的數。

你的工作是對於輸入的集合S去尋找“丑數集合”中的第N個“丑數”。

輸入格式

第 1 行: 二個被空格分開的整數:K 和 N,1<= K<=100 ,1<= N<=100,000。

第 2 行: K 個被空格分開的整數:集合S的元素。

輸出格式

單獨的一行,輸出對於輸入的S的第N個丑數。所有輸出答案可以用longint(32位整數)存儲。

輸入樣例

4 19

2 3 5 7

輸出樣例

27

        (1)編程思路。

        本題在習題14-1的基礎上更進一步,可以指定多個質數,這些質數來自集合S。因此在習題14-1的基礎上,用數組p[101]來代替p1、p2和p3,采用循環處理即可。具體參見源程序。

        (2)源程序。

#include <stdio.h>

int main()

{

         int p[101],a[101],H[100005];

         int k,n,i,j;

         scanf("%d%d",&k,&n);

    H[0] = 1;

    for (i=1;i<=k;i++)

    {

        scanf("%d",&p[i]);

        a[i]=0;

    }

    for (i = 1;i<=n;i++)

     {

                   H[i] = p[1]*H[a[1]];

                   for (j=2;j<=k;j++)

                         if (H[i]>p[j]*H[a[j]]) H[i]=p[j]*H[a[j]];

                    for (j=1;j<=k;j++)

                        if(H[i]==p[j]*H[a[j]])  a[j]++;

     }

    printf("%d\n",H[n]);

    return 0;

}

14-3  集合S

問題描述

一個集合S有如下元素:1是集合S的元素;若x是集合S的元素,則2x+1,3x+1也是集合S的元素。將集合S的元素按從小到大排列后,問第N個元素是多少?

輸入格式

輸入包含多個數據,每個數據為一個正整數N (1 <= N <= 10000000)。.

輸出格式

對每個N,在單獨一行輸出集合S中第N個元素。

輸入樣例

100

254

輸出樣例

418

1461

        (1)編程思路。

        這道習題和例14的解題思路是完全一樣的。兩個指針p2和p3從0開始起頭並進,一個表示 x2+1,另一個表示x3+1,哪個小前進哪個,如果兩個相等就都前進。這樣就可以產生出遞增的不重復的序列。

        定義數組int num[10000001]保存產生的這個序列的前10000000項,這樣輸入n后,直接輸出數組元素num[n]即可。

       (2)源程序。

#include <stdio.h>

int num[10000001];

int main()

{

        int p2,p3,i,min,n;

        num[1]=1;

         p2=p3=1;

         i=1;

         while(i<10000000)

         {

                min=2*num[p2]+1;

                if (min>3*num[p3]+1) min=3*num[p3]+1;

                num[++i]=min;

               if(num[i]==2*num[p2]+1)  p2++;

               if(num[i]==3*num[p3]+1)  p3++;

         }

         while (scanf("%d",&n)!=EOF)

         {

                printf("%d\n",num[n]);

         }

    return 0;

}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM