欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

C语言程序设计100例之(14):丑数

程序员文章站 2022-06-11 16:05:19
例14 丑数 问题描述 丑数是其质因子只可能是2,3或5的数。前10个丑数分别为1, 2, 3, 4, 5, 6, 8, 9, 10, 12。输入一个正整数n,求第n个丑数。 输入格式 每行为一个正整数n (n <= 1500),输入n=0结束。 输出格式 每行输出一个整数,表示求得的第n个丑数。 ......

例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;

}