Description
zyf最喜歡的數字是1!所以他經常會使用一些手段,把一些非1的數字變成1,並為此得意不已。他會且僅會的兩種手段是:
1.把某個數m除以某個質數p——當然p必須能整除這個數,即m=m/p
2.把某個數m減1,即m=m-1
有一天他突發奇想,想把[a,b]區間中所有的數一個一個地變成1,這是一個巨大的無聊的工程,所以他想知道他最少得花多少操作才能達到目的。
Input 輸入包含多組資料(1000組資料),EOF結束。
每組資料以兩個整數開頭:a,b(0<a<=b<=100000),意義如題意描述。Output 每組資料輸出一行,最少運算元。Sample Input2 3
3 5
11 12Sample Output2
4
3
問題分析:要求出把a,b之間所有數變成1的最少步驟,肯定得計算出每個數字變成1的最少步驟。那麼如何求出一個數字通過題中兩種運算變成1的最小步驟呢?分析到這,我們腦中肯定展現出一個樹狀結構出來,每個節點是當前的數字,而他可以有未知個孩子節點,包括減一操作以及除以的所有的素因子的操作。於是我們感覺到問題的複雜,求一個數字變成1的方法尚且這麼複雜,還讓求出最少步驟。即使算出來了,也很費時啊。引用小平爺爺的一句話:沒有調查就沒有發言權!1.其實認真想一下,一個數位素因子是有限的,據2*3*5*7*11*13<100000<2*3*5*7*11*13*17可以判斷100000以內的數字最多有6個素因子。所以我們的分支沒有那麼多。2.不要忘了任何一個素數只需一步就可以變成1。100000以內大概有一萬個素數,用素數篩選法計算出素數後,我們已經解決了一萬個數字了。而且後面假設用遞迴或者動態規劃也很容易的可以使用剪枝了。3.計算n需要多少步驟時,其實是比較n-1,n/p……(p為素因子)中最小步驟的然後加一即可。到此大概演算法已經有了。解法一:果斷可以遞迴嗎,最樸素的演算法沒有最佳化,需要10秒左右才可以算完100000以內的所有數。解法二:在素數篩選後把素數弄到素數表中存起來,減少判斷素數的時間,同時去掉函數的遞迴調用,因為可以從小的數往大的數算,也即是迭代式的。效率提高到3秒。
int search_deep(int num){if(data[num] != 0){return data[num];} else {int temp = data[num - 1] + 1;int i = 0;/* 解法一for(i = num / 2; i > 1; i--){if(!prime[i] && num % i == 0 && search_deep(num / i) + 1 < temp){//temp = search_deep(num / i) + 1;temp = data[num / i] + 1;}}*/// 解法二for(i = 1; prime_array[i] <= num / 2; i++){if(temp <= 2)break;if(num % prime_array[i] == 0 && data[num / prime_array[i]] + 1 < temp){temp = data[num / prime_array[i]] + 1;}}return temp;}return 0;}
解法二做了大量最佳化已經達到了3秒的時間效率,可是還是太慢,其實主要原因的總是從最小素數開始判斷是否為素因子,感覺接近1的速度就會慢下來。如果能從最大的素因子開始判斷就更好了,可是似乎沒什麼解決辦法。罷了。 解法三:類似於素數篩選法,著名的素數篩選法應該都知道不用解釋。那麼也可以利用這個思想來解決這個問題。如果數n變成1的步驟為x,那麼n乘以任何一個素數的步驟都是x+1。這個思想似乎是真正的迭代,先解決小問題,再解決大問題,速度要高很多!代碼如下:
void generate(){prime[1] = 1;prime[2] = 0;prime[3] = 0;int i = 0;int j = 0;int k = 1;for(i = 2; i < 100001; i++){if(prime[i] == 0){data[i] = 1;for(j = 2; i * j < 100001; j++){prime[i * j] = 1;}prime_array[k++] = i;}}for(i = 2; i <= 100000; i++){if(data[i - 1] + 1 < data[i])data[i] = data[i - 1] + 1;for(j = 1; j < k && 1.0 * i * prime_array[j] < 100001; j++){if(data[i] + 1 < data[i * prime_array[j]] || data[i * prime_array[j]] == 0){data[i * prime_array[j]] = data[i] + 1;}}}}
解法三的執行效率一下提高到了10ms左右!驚呼啊!主要是解法三的判斷條件讓程式去掉了很多沒用的計算。