寻找丑数问题 HDOJ 1058 Humble Numbers
扫描二维码
随时随地手机看文章
这道题目曾经也是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#includeusing 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 && 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; }