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

例14   醜數

問題描述編程

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

輸入格式less

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

輸出格式指針

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

輸入樣例ip

1input

2string

50io

0

輸出樣例

1

2

243

         (1)編程思路。

        根據醜數的定義,醜數從小到大排列的序列中的一個數應該是其前面某個數乘以二、3或者5的結果。所以,能夠定義一個數組num[1501]來順序保存序列中的醜數,數組裏面的每個元素的值是前面的某個元素值乘以二、3或者5獲得。

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

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

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

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

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

        初始時,num[1] = 一、index =二、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個元素生成後,須要對指針p二、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,三、5參數化爲p一、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。這個正整數集合包括,p一、p1*p二、p1*p一、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]來代替p一、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;

}

相關文章
相關標籤/搜索