这道题目曾经也是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]中可能会有两个同样的值是最小的,下面这小段代码解决了方法一中遇到的丑数重复的问题。
- <span style="white-space:pre"> </span>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;
- }