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;

}

原文地址:https://www.cnblogs.com/cs-whut/p/11902366.html

时间: 2024-11-05 14:49:19

C语言程序设计100例之(14):丑数的相关文章

经典C语言程序设计100例 -- C 和 Python 版 (06 - 10)

[06]格式化输出 题目:用*号输出字母C的图案. 思路:可先用'*'号在纸上写出字母C,再分行输出.如果输出图形较大,且有规律可循,可考虑使用循环. C 语言代码 int main() { const char *p = " **** \n" " ** ** \n" "** \n" "** \n" "** \n" " ** ** \n" " **** \n"; pr

黑马程序员——经典C语言程序设计100例

1.数字排列 2.奖金分配问题 3.已知条件求解整数 4.输入日期判断第几天 5.输入整数进行排序 6.用*号显示字母C的图案 7.显示特殊图案 8.打印九九口诀 9.输出国际象棋棋盘 10.打印楼梯并按条件打印笑脸 11.经典兔子问题 12.判断素数 13.水仙花数问题 14.正整数分解质因数 15.学习成绩划分 16.正整数求其最大公约数和最小公倍数 17.统计英文字母/空格/数字个数 18.求s=a+aa+aaa+aa...a的值 19.求解"完数" 20.球体自由落下物理问题

C语言程序设计100例之(3): Cantor表

例3    Cantor表 题目描述 现代数学的著名证明之一是Georg Cantor证明了有理数是可枚举的.他是用下面这一张表来证明这一命题的: 1/1  1/2  1/3  1/4  …… 2/1  2/2  2/3  …… 3/1  3/2  …… 4/1  …… …… 现以z字型方法给上表的每项编号.方法为:第一项是1/1,然后是1/2.2/1.3/1.2/2.1/3.1/4.2/3……. 输入格式 整数N(1≤N≤10000000) 输出格式 表中的第N项 输入样例 7 输出样例 1/

C语言程序设计100例之(9):生理周期

例9    生理周期 问题描述 人生来就有三个生理周期,分别为体力.感情和智力周期,它们的周期长度为 23 天.28 天和33 天.每一个周期中有一天是高峰.在高峰这天,人会在相应的方面表现出色.例如,智力周期的高峰,人会思维敏捷,精力容易高度集中.因为三个周期的周长不同,所以通常三个周期的高峰不会落在同一天.对于每个人,我们想知道何时三个高峰落在同一天. 对于每个周期,我们会给出从当前年份的第一天开始,到出现高峰的天数(不一定是第一次高峰出现的时间).你的任务是给定一个从当年第一天开始数的天数

C语言程序设计100例之(21):折半查找

例21  折半查找 问题描述 顺序查找是一种最简单和最基本的检索方法.其基本思想是:从检索表的一端(如表中第一个记录或最后一个记录)开始,逐个进行记录的关键字和给定值的比较.若某个记录的关键字和给定值比较相等,则查找成功:否则,若直至检索表的另一端(如最后一个记录或第一个记录),其关键字和给定值比较都不等,则表明表中没有待查记录,查找不成功. 顺序查找可以写成一个简单的一重循环,循环中依次将检索表(不妨设为数组a)中的元素与给定值比较,若相等,用break退出循环.算法描述为: for (i=0

C语言程序设计100例之(22):插入排序

例22  插入排序 问题描述 排序是计算机程序设计中的一种重要操作,它的功能是将一个数据元素或记录的任意序列,重新排列成一个以关键字递增(或递减)排列的有序序列. 排序的方法有很多,简单插入排序就是一种简单的排序算法. 插入排序的基本思想是顺序将一个待排序的记录按其关键字值的大小插入到一个有序的序列中,插入后该序列仍然是有序的. 简单插入排序是一种最简单的排序方法.它的排序过程为:先将待排序序列中第1个记录看成是一个有序的子序列,然后从第2个记录起依次逐个地插入到这个有序的子序列中去.这很像玩扑

C语言程序设计100例之(4):水仙花数

例4    水仙花数 题目描述 一个三位整数(100-999),若各位数的立方和等于该数自身,则称其为“水仙花数”(如:153=13+53+33),找出所有的这种数. 输入格式 没有输入 输出格式 若干行,每行1个数字. 输入样例 无 输出样例 153 * * * ... * * * (输出被和谐了) (1)编程思路1. 对三位数n(n为100~999之间的整数)进行穷举.对每个枚举的n,分解出其百位a(a=n/100).十位b(b=n/10%10)和个位c( c=n%10),若满足a*a*a+

C语言程序设计100例之(13):最大子段和

例13        最大子段和 题目描述 给出一段序列,选出其中连续且非空的一段使得这段和最大.例如在序列2,-4,3,-1,2,-4,3中,最大的子段和为4,该子段为3,-1,2. 输入格式 第一行是一个正整数N,表示了序列的长度. 第二行包含N个绝对值不大于10000的整数Ai ,描述了这段序列. 输出格式 一个整数,为最大的子段和是多少.子段的最小长度为1. 输入样例 7 2 -4 3 -1 2 -4 3 输出样例 4 (1)编程思路. 可以从长度为n的数列的最左端(设为数组元素a[1]

C语言程序设计100例之(20):过河卒

例20  过河卒 题目描述 如图1,在棋盘的A点有一个过河卒,需要走到目标B点.卒行走规则:可以向下.或者向右.同时在棋盘上的任一点有一个对方的马(如图1的C点),该马所在的点和所有跳跃一步可达的点称为对方马的控制点.例如,图1中C点上的马可以控制9个点(图中的P1,P2,…,P8 和C).卒不能通过对方马的控制点. 棋盘用坐标表示,A点(0,0).B点(n,m)(n,m为不超过50的整数,并由键盘输入),同样马的位置坐标通过键盘输入,并约定C<>A,同时C<>B. 编写程序,计算