題目:鏈接
卡拉茲(Callatz)猜想已經在1001中給出了描述。在這個題目里,情況稍微有些復雜。
當我們驗證卡拉茲猜想的時候,為了避免重復計算,可以記錄下遞推過程中遇到的每一個數。例如對 n=3 進行驗證的時候,我們需要計算 3、5、8、4、2、1,則當我們對 n=5、8、4、2 進行驗證的時候,就可以直接判定卡拉茲猜想的真偽,而不需要重復計算,因為這 4 個數已經在驗證3的時候遇到過了,我們稱 5、8、4、2 是被 3“覆蓋”的數。我們稱一個數列中的某個數 n 為“關鍵數”,如果 n 不能被數列中的其他數字所覆蓋。
現在給定一系列待驗證的數字,我們只需要驗證其中的幾個關鍵數,就可以不必再重復驗證余下的數字。你的任務就是找出這些關鍵數字,並按從大到小的順序輸出它們。
輸入格式:
每個測試輸入包含 1 個測試用例,第 1 行給出一個正整數 K (<100),第 2 行給出 K 個互不相同的待驗證的正整數 n (1<n≤100)的值,數字間用空格隔開。
輸出格式:
每個測試用例的輸出占一行,按從大到小的順序輸出關鍵數字。數字間用 1 個空格隔開,但一行中最后一個數字后沒有空格。
輸入樣例:
6 3 5 6 7 8 11
輸出樣例:
7 6
剛開始還想着通過打表的方式記錄下3n+1猜想里的全部數字,並作標記,后來感覺越做越麻煩……后來看了別人的思路后發現,自己的標記用的不太對,他們都是越用越簡單,我卻是越用越復雜,就是太菜了T^T。痛定思痛,重新捋了下思路。
思路:
- 用數組存儲輸入的數字后直接對其進行3n+1猜想,然后將其衍生出來得到的數字做上標記,其本身不需要做上標記,因為如果其本身如果是其他數的衍生數,就會被覆蓋掉,即也會被做上標記。如此遍歷后,沒有被做上標記的數就是需要被輸出的數,那么只要進行從大小的排序后就可以輸出了,注意格式就行。
代碼:
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #include <sstream> 5 #include <cmath> 6 #include <algorithm> 7 #include <string> 8 #include <stack> 9 #include <queue> 10 #include <vector> 11 #include <map> 12 using namespace std; 13 14 bool cmp(int a, int b) 15 { 16 return a > b; 17 } 18 19 int main() 20 { 21 int flag[100005]; 22 int k,n[105]; 23 int cnt[105]; 24 memset(cnt, 0, sizeof(cnt)); 25 memset(flag, 0, sizeof(flag)); 26 scanf("%d", &k); 27 for(int i = 1; i <= k; i++) 28 { 29 int t; 30 scanf("%d", &n[i]); 31 t = n[i]; 32 while(t > 1) 33 { 34 if(t % 2 == 0) 35 t /= 2; 36 else 37 t = (3 * t + 1) / 2; 38 flag[t] = 1; 39 } 40 } 41 int j = 0; 42 for(int i = 1; i <= k; i++) 43 { 44 if(flag[n[i]] == 0) 45 { 46 cnt[j] = n[i]; 47 j++; 48 } 49 } 50 sort(cnt, cnt + j, cmp); 51 for(int i = 0; i < j; i++) 52 { 53 if(i != j-1) 54 printf("%d ", cnt[i]); 55 else 56 printf("%d\n", cnt[i]); 57 } 58 return 0; 59 }
總結:
在測試點4錯了好幾次,后來居然發現是3n+1猜想里為奇數時算錯了T^T,要細心點啊,而且通過這么多天發現,我確實是菜,巨菜,還是先從PAT乙級的難度開始吧。萬事都要循序漸進,急於求成只會南轅北轍。