把只包含質因子 2、3、5 的數稱為醜數。習慣上 1 是第一個醜數。求按從小到大順序的第 N 個醜數(1 ≤ N ≤ 1500)。
逐個自然數檢測是否為醜數,反覆除以 2、3、5 直到不能整除,最後判斷是否為 1。時間複雜度極高,N=1500 時嚴重超時。
核心洞察:每個醜數都是前面某個醜數乘以 2、3 或 5 得到的。維護三個指針 p2、p3、p5,分別指向「乘以 2/3/5 後大於當前最大醜數」的最小位置。
ugly[0] = 1,三指針均指向索引 0next2 = ugly[p2]*2、next3 = ugly[p3]*3、next5 = ugly[p5]*5next = min(next2, next3, next5)void generateUglyNumbers() {
ugly[0] = 1;
int p2 = 0, p3 = 0, p5 = 0;
for (int i = 1; i < 1500; ++i) {
int next2 = ugly[p2] * 2;
int next3 = ugly[p3] * 3;
int next5 = ugly[p5] * 5;
ugly[i] = min(next2, min(next3, next5));
if (ugly[i] == next2) ++p2;
if (ugly[i] == next3) ++p3;
if (ugly[i] == next5) ++p5;
}
}
| 步驟 | ugly 數組 | p2/p3/p5 指向值 | 候選值 (×2/×3/×5) | 選中 |
|---|---|---|---|---|
| 初始 | [1] | 1 / 1 / 1 | 2 / 3 / 5 | 2 |
| 1 | [1, 2] | 2 / 1 / 1 | 4 / 3 / 5 | 3 |
| 2 | [1, 2, 3] | 2 / 3 / 1 | 4 / 6 / 5 | 4 |
| 3 | [1, 2, 3, 4] | 3 / 3 / 1 | 6 / 6 / 5 | 5 |
| 4 | [1, 2, 3, 4, 5] | 3 / 3 / 2 | 6 / 6 / 10 | 6 |
| 5 | [1, 2, 3, 4, 5, 6] | 4 / 4 / 2 | 8 / 9 / 10 | 8 |
int 足夠(< 231)while(scanf("%d", &n) != EOF) 循環讀取醜數問題是動態規劃與多指針技巧的經典結合。關鍵在於識別「有序序列由歷史元素乘因子生成」的遞推結構,並用三指針高效去重合併。這種「多路歸併」思想廣泛應用於堆、優先隊列等場景。