寻找丑数问题 HDOJ 1058 Humble Numbers

本文探讨了一种高效算法,用于找出第5842个丑数。丑数是指只包含质因数2、3、5、7的正整数。文章通过定义四个游标跟踪丑数的生成过程,并采用一种巧妙的方法避免了重复计算和乱序问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这道题目曾经也是Google、Hulu的一道面试题。对于这道题目,关键是要能够想到这一点:即对于任意一个丑数f[i],它都是由它前面的丑数乘以2,3,5或者7得到的。那么如果我们已经得到f[0] 到 f[i],怎样才能得到f[i+1]呢? 

可能你会怎么想(我刚开始也是这么想的)。我们已经有第一个丑数f[1] = 1,那么就设一个游标i=1指向f[1],然后依次将2,3,5,7与f[i]相乘,即我们可以得到2,3,5,7,然后依次将这些丑数加入到f[i]中,f[2] = 2, f[3] = 3, f[4] = 5, f[5] = 7,接着讲游标i++指向2,再分别于2,3,5,7相乘,即可以得到f[6] = 4, f[7] = 6, f[8] = 10, f[9] = 14。按照这种算法算下去,直到丑数的个数累计到了5842个。 仔细再想,我们会发现这种算法有两个问题,第一:所得的丑数数组是乱序的,即我们还需要对其进行排序,不过这个问题不大;更为严重的问题是数组中存在重复的丑数:前面我们已经得到了f[7] = 6,如果按以上算法继续算下去的话,很容易我们也可以得到f[10] = f[3] * 2= 6.所以我们排序完成之后要将数组中重复的数组去掉,去除完了之后又要进行新一轮的迭代,来使丑数数组中的不重复丑数达到5842个以上,很显然这个是很难用程序去控制的,因为我们需要事先计算出数组到底有多少重复的丑数,不过按照这个规律的话应该是可以算出来,不过我还没想到怎么算。^_^

那么有更好的解决方案吗? 这里给出了一个比较好的解决办法。 :

即定义四个游标,我们记为pos1,pos2,pos3,pos4 ,初始均为1. 这四个游标分别跟踪着对应的丑数乘以2,3,5,7所得的值。我们已经看到了方法一会给我们带来乱序和丑数重复的问题,方法二可以很好地解决这两个问题。 我们由一中的分析已经得到了f[i+1]是由f[1]到f[i]中的某个数乘以2,3,5,7中的某个数得来的。具体解法见以下代码,需要注意的是,arr[i]中可能会有两个同样的值是最小的,下面这小段代码解决了方法一中遇到的丑数重复的问题。

		for(int j=0; j<N; j++)
			if(flag[j])
				pos[j]++;


对其进行了简单的修改,当然解题思想是参考该作者的。

#include<iostream>
#include<string>
using namespace std;

int const N = 4;
int map[N] = {2,3,5,7};

int findMin(int arr[], bool flag[], int len)
{
	int min = arr[0];
	int i=0;
	for(i=0; i<len; i++)
		if(arr[i] < min)
			min = arr[i];
	for(i=0; i<len; i++)
	{
		if(min == arr[i])
			flag[i] = true;
		else
			flag[i] = false;
	}
	
	return min;
}


void initHumble(int maxN, int humble[])
{
	humble[1] = 1;
	int pos[N] = {1,1,1,1};
	int arr[N];
	bool flag[N];

	for(int i=2; i<=maxN; i++)
	{

		for(int j=0; j<N; j++)
			arr[j] = humble[pos[j]]*map[j];

		int min = findMin(arr, flag, N);
		humble[i] = min;
		for(int j=0; j<N; j++)
			if(flag[j]) //相同的最小丑数在这里会被过滤掉
				pos[j]++;
		
	}
}

void outPut(int humble[])
{
	int n;

	while(cin>>n && n)
	{
		string str = "th";
		if(n%10==1 && n%100!=11)
			str = "st";
		else if(n%10==2 && n%100!=12)
			str = "nd";
		else if(n%10==3 && n%100!=13)
			str = "rd";

		cout<<"The "<<n<<str<<" humble number is "<<humble[n]<<"."<<endl;
	}
}

int main()
{
	const int SIZE = 6000;

	int humble[SIZE];

	initHumble(5842, humble);
	
	outPut(humble);

	return 0;
}



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值