pat乙級自我回顧:一般錯誤出現原因

2023-03-11 06:00:38

在obsidian裡面寫的有些參照沒用,需要的可以評論區或者私信我呦~
對於錯誤,末尾的換行不影響格式,

段錯誤:

一般是設定的陣列小於題目給定的要求,迴圈條件i--寫成i++,陣列下標寫錯,也有可能是因為陣列a沒有初始化,導致 b[a[2]] 這種形式存取了⾮法記憶體,是否沒有考慮0或者邊界值的情況?⽐如對於⼀個空陣列卻存取了 arr[0]即,scanf的時候是不是沒寫&陣列越界、還有就是堆疊溢位(⽐如,遞迴調⽤層數太多)

答案錯誤一般就是程式碼邏輯有錯誤,或漏了某個點,從新審題把孩子

執行超時:

  1. 所有測試點都是運⾏超時,⼀般情況是出現了死迴圈
  2. 部分說明題目不能用暴力破解,嘗試跳過一些數.
  3. 然後就是當你對一段資料重複存取,你應該問問自己怎麼樣減少存取次數。
  4. cin 的輸⼊⽐ scanf 更耗時,如果個別測試點超時可以考慮是否可以通過優化輸⼊將 cin 改成 scanf
  5. 使⽤ map 儲存如果超時了,⽽題⽬⼜沒有要求排序,換成⽤ unordered_map 就不會超時,同理,使⽤ set 儲存如果超時了,可以考慮換成 unordered_set 避免超時。
  6. 對資料進⾏排序的過程中,有很多資料是⽆效資料,如果對所有資料都排序可能會引起超時,所以可以考慮在放⼊陣列之前就做⼀些條件判斷剔除⽆⽤資料,然後再對陣列進⾏排序。
  7. 一些小習慣:如果寫了 main 函數之外的其他函數,傳引⽤會⽐傳值的⽅式更省時,因為傳值是拷⻉傳參,傳引⽤是傳⼊的地址直接在原變數上修改,所以可以考慮優化為 void func(int& a) 的⽅式傳遞引數減少耗時。for 迴圈語句⾥定義 int i 和在外⾯定義 int i 其實有少許區別,在 for 迴圈語句⾥直接定義的 i ,⽐如 for(int i = 0; i < 10; i++) 可能會更耗時,把它改成 int i; for (i = 0 ; i < 10; i++) 也是降低程式碼運⾏時間的⼀種可考慮⽅式哦~
  8. 使⽤了 v.size()-1 等⽅式,如果 v.size() 本身就是0,因為 v.size() 返回值是⽆符號整數 unsigned int , unsigned int 的0減去1得到的是 unsigned int 的最⼤值⽽⾮ -1 ,這可能會讓int i 迴圈時 i < v.size() - 1 超時
    https://www.cnblogs.com/ahappyfool/p/17173405.html
    如果某些迴圈是必要的,那麼可以採取跳著存取,跳兩個效率就提升一倍。例如1104緣份數:m和n的最大公因數必須大於2,容易得知s1最後結果的個位一定需要是9,如若不是9則n=m+1,那麼n與m的最大公因數一定只能是1,因為相鄰兩個正整數的最大公因數是1,只有s1的末位是9或末尾是連續的9,加一之後末尾變為0才有可能使m與n的最大公因數大於2。[程式碼#1104 天長地久]
    常考錯誤點和方法統計:
    一定要注意自己寫的每一步,for內部,函數引數,判斷條件等都是容易犯錯的地方——特別注意已經非常多次了
    注意給的數值範圍,如果儲存不下換型別,注意不超過等字眼,注意沒有給定什麼型別的數,然後就是某種型別不滿足要求就該使用(int->double)1020 月餅:[[程式碼#1020 月餅:不能在結構體內使用其他結構體變數初始化另一個變數,可以指定預設值。注意資料型別,這種銷售資料不能時int]]
    關注題目資訊
    注意臨界情況測試,如1103緣份數中,對於m=1時,如果b也等於1,那麼就會出錯,會得到答案不符的結果,所以測試一下就好了
    題⽬輸⼊中所給的a b區間可能a和b給的順序反了,要⽐較⼀下a和b的⼤⼩,如果 a>b 記得swap回來。
    如果是簡單題觀察能否直接使用某種方式打出,又或者說,能不能一點一點打出
    特殊情況處理(空格處理,特例,哪種情況特殊),處理時因十分注意,以免有有坑,即把不是特殊情況的情況處理成特殊情況1012 數位分類:[[程式碼#1012 數位分類:要注意當沒有該類元素時才用n表示,也就是說該小心當總和為零的選項]]
    大量讀入和輸出使用scanf和printf 1066 影象過濾
    %操作要注意負數1069微博轉發抽獎(和map)
    定義大陣列要使用全域性變數⽽不是寫在main函數⾥⾯,否則容易發⽣段錯誤(因為⼤陣列
    在 main 函數⾥⾯的話是儲存在棧⾥,⽽棧空間是在程序建立時初始化的,有固定的⼤⼩,⼀
    般為⼏⼗KB,所以太⼤的陣列會耗光棧空間。⽽全域性變數佔⽤的堆空間,堆空間中的記憶體是按
    需分配,⾃由增⻓的,可以⾮常⼤,⽐如32位元的系統中可以⼤到4GB。將⼤陣列放在全域性變數中
    能避免棧溢位~同時對於做題目可以嘗試把重複的元素和固定不變的元素都放在main之外
    如果覺得陣列從下標0開始麻煩,可以考慮捨棄0位,從1下標開始儲存資料
    int最⼤值為2147483647,共10位數位; LLONG_MAX 最⼤值有19位數位,以9開頭。所以說儲存13位的學號可以⽤ long long int ,輸出的時候使⽤ %013lld
    對 char c[15] 進⾏ sort 排序的 cmp(char a[], char b[]) 函數要這樣寫: strcmp(a, b) < 0 ,因為 strcmp 返回的不是 -1 0 1 ⽽是 等於0 ⼩於0 ⼤於0 的某個值, strcmp 在頭⽂件 #include <string.h> ⾥⾯對 vector v 或者 string v 進⾏ sort 排序: sort(v.begin(), v.end(), cmp1); ;對陣列a進⾏sort排序: sort(a, a + n, cmp1);
    輸出語句如果有 is / are、加s / 不加s的區別的話⼀定要當⼼,⽐如 "There is 1 account" 和 "There are 2 accounts" 是不同的輸出語句
    printf輸出%的時候用%%
    當索引為int時,使用陣列比map好得多,尤其是在時間嚴格的時候
    時間和日期需要運算比較時可以將其轉化為最小單位如秒和天,也可以不轉,看哪個方便吧。學⽣姓名和分數,如果不需要將姓名輸出的話,不僅可以⽤ struct 結構體中 string 或者 char 儲存姓名,還可以將姓名轉化為 int 型別,⽐如 ABC4 可以轉換為 ((((A * 26) + B) * 26) + C) * 10 + 4 = ((((1 * 26) +2) * 26) + 3) * 10 + 4
    大的數的後面幾位數等於小的數:1.to_string 2.%操作
    對於多個迴圈體內多個變數需要累加求和時,應該在最後一重回圈進行運算,否則可能會因為當前迴圈進行多遍而導致某個中間數加了多遍。
    整除和被整除不同,別搞反了
    是否產生相同元素,產生是否想要的結果
    1106題外話:判斷數列中不可能出現2018;通過奇偶判斷:奇偶會出現規律:0011,0110,1100,1000,0001, 0011,0110
    gcd:輾轉相除法求最大公約數
int gcd(int a, int b){	return b == 0 ? a : gcd(b, a % b);}

字串:stoi,to_string,substr,data,reverse,sort,insert,

  1. 實現數學計算(int):注意開頭零。注意含有空格可能(getline和cin.get()迴圈連續輸入多行可能包含空格的字串,無論在此之前是否有讀入,應該在迴圈開始前加入cin.get();)。還要當心結果為0(即去開頭0不能把結果為0去掉)要符合邏輯,保證應有的結果。如果前面保留有前導零,和數位太長就是說明該用字串了。1114全素日。1019數位黑洞(atoi和to_string):[[程式碼#1019 數位黑洞:注意字串轉數位和數位轉字串也就是還有前導零,然後當n=6174時,不能直接輸出空值,可以嘗試自己定義轉換函數也可以用庫]] 1017 A除以B:(同時還要小心當被除數只有一位,和為0,為負數的情況)。部分簡單題直接改為long long就行。大整數運算注意0[[程式碼#1017 A/B:注意當除數沒有被除數大時應該輸出0,而且首部不能有多餘的0;模擬手動除法的過程,每次用第一位去除以B,如果得到的商不是0就輸出,否則就 *10+下一位,直到最後的數為餘數]]
  2. 實現數學計算(double):浮點數的⽐較不能直接⽤ == ⽐較,因為根據計算機中浮點數的表示⽅法,對於同⼀個⼩數,當⽤不同精度表示時,結果是不⼀樣的,⽐如 float 的 0.1 和 double 的 0.1 ,直接⽤ == ⽐較就會輸出不相等,所以可以使⽤⾃定義 eps = 1e-8 進⾏浮點數的⽐較:對於保留小數輸出時要注意輸出-0.00;比如說-0.004保留兩位小數就是,要避免的話if(p3>-0.005&&p3<0)p3=0;加個判斷條件
  3. 實現時間和日期,數位相加有(60s,60m,24h向前進一和日期也是,數位的四捨五入round,如果是整數轉為int,注意高位加一。然後就是輸出格式問題
  4. 使用c語言字串應該注意結尾0,而string中沒有,因此在字元陣列定義時要多給幾個空間避免出錯。
  5. 字串對應輸出時可以使用字串陣列,不要用switch case和條件判斷。int變成string使用to_string函數,如果還要對應輸出就可以str[num[i] - '0'];轉到對應字元陣列輸出就行,PAT乙級1002 題寫出這個數(與柳神思路比對),同理要進行多個數計數是用陣列儲存他們string(保證位數時注意前導零)或者int陣列。當然太少也可以不用儲存。1006 換個格式輸出整數:[[程式碼#1006 不能對一大串括號整數運算使用++、--]]如果不是隻有百位 1014 福爾摩斯的約會:[[程式碼#1014 福爾摩斯的約會:注意條件判斷,什麼字元該通過,什麼不該,然後就是時間的變化進位,和格式化輸出,使用字串陣列儲存要輸出的星期]]
  6. 邏輯推理判斷符合的字串:1003 我要通過’PAT‘(找規律,數學題):[[程式碼#1003 我的想法錯誤的原因時:每次A3並不是直接減兩倍,而是減A1那麼大。所以A3/A1要等於A2]]
  7. 遍歷找尋符合要求的字元,注意通過字元的條件判斷 1014 福爾摩斯的約會(上面第五點)
  8. 1009 說反話 :[[程式碼#1009 思路一:直接儲存字串使用scanf的while迴圈while(scanf("%s",&str[i])!=EOF){ i++; }有空格所以用字元陣列,或者使用vector 來儲存。while(cin >> str),然後反向輸出,思路二:使用堆疊,push和pop思路3:使用fgets()方法讀取這一行字串,存到一個一維陣列中,之後遍歷這個陣列,以空格為判斷條件,分別存入一個二維陣列中,之後逆序輸出這個陣列。fgets(char *str,int num,FILE *stream);]]
  9. 給⼀些數位字串,求這些字串拼接起來能夠構成的最⼩的數位的⽅式:⽤ cmp 函數就能解決: bool cmp(string a, string b)
  10. 通過空格和標點分隔每個獨立單詞:遍歷字串,使用臨時變數儲存(+=)每個不是標點或空格的字元,當讀到空格或標點時對單詞進行操作,並清空tmp
  11. char a[] 的⻓度要⽤ strlen(a) ; 得到的⻓度是⾥⾯真實儲存了字⺟的⻓度,不包括 \0 要輸⼊⼀⾏的資料的話: 如果是 string s ,則⽤ getline(cin, s); 在頭⽂件 #include ⾥⾯; 如果是 char str[100] , 則⽤ cin.getline(str, 100); 在頭⽂件 #include ⾥⾯,也可以⽤ gets(str);#todo
  12. 關於字串提取有效元素:1052 賣個萌 1024 科學計數法 1058 選擇題 1073 多選題常見計分法 1068 萬綠叢中一點紅:#todo

實際問題多重回圈列舉邏輯條件判斷處理(模擬):

  1. 迴圈內判斷條件等於n,某個數不一定是和其他一樣是整數。注意使用數學邏輯演算,多重回圈某數相加,最後加不用+=,以免重複加
  2. 當某些操作要移動多個位置,一個迴圈解決不了時,可一點一點移動使用for迴圈一位一位移動。1008 元素迴圈右移(2.逆序3.重複交換4.直接列印,先從逆轉那打到尾,在從頭打,很多時候直接列印出來會簡單很多):[[程式碼#1008元素迴圈右移(三種方法):思路一:利用遞迴或者回圈一次一次移動;思路二:將所有元素逆序,然後將前m位和後n-m位分別逆序]]
  3. 狼人殺,模擬,邏輯題,這類題要注意題目是要求怎麼怎麼優先輸出。他說的是按狼人序列小的,所以說,你一開始使用誰說謊來進行判斷就會徒增麻煩。注意條件控制,這種題目其實不難。[[程式碼#1087 狼人殺:]]

優先,排序,大量資料查詢操作:

  1. 當大量的for迴圈是用來查詢的時候就是該使用排序和二分查詢的時候或者使用大陣列下標
  2. 只要輸出最大最小或者少量資料時可以不用排序,使用中間變數記住就行。1004 成績排名:[[程式碼#1004 要注意它說的字串不超過十個字元,加上‘ 0’也就是11個;vector傳參照為函數引數,然後vector在宣告是並不會直接呼叫建構函式,然而在用c語言陣列時會出現無無參建構函式的錯誤,當然也可以不寫建構函式改為寫複製建構函式。類定義完要加;]]
  3. 對於題目要求很明顯排序,這個時候一定要耐下性子來分析關係,寫出合適的com函數,然後return可以寫數1<數2來減少程式碼量 1015 德才論:加了一個level引數[[程式碼#1015 德才論:通過多組資料進行排序,主要考察對資料的理解寫出com函數]]
  4. 1100校慶(排序校友,在來賓中使用二分查詢是否校友,因為還要統計非校友年齡最大所以這樣遍歷較為省事) 重點是確定好排序和二分查詢的物件
  5. map的使用:1003pat中元素(字元)個數對應<char,int>;1069 微博轉發抽獎(stl容器一般自動初始化為0或空);1080 mooc最終成績(使用map來更好的對應輸入實現map的鍵值排序)。當不需要排序時,使用unordered_map可以節省時間
  6. 遇到連結串列中有⽆效結點還要排序的,在結構體⾥⾯加⼀個 bool flag = false; 變數,將有效結點的 flag 標記為 true ,並統計有效節點的個數 cnt ,然後在 cmp 函數中這樣寫:
   bool cmp(node a, node b) {  
   	if(a.flag == false || b.flag == false)  
   		return a.flag > b.flag;  
   	else  
   		return a.value < b.value;  
   }

這樣 flag == false 的會⾃動變換到最後⾯去,到時候輸出前 cnt 個有效的結點就可。如果問年齡區間內的前100個,⽽資料量很龐⼤的話,在處理資料的時候就可以把每個年齡的前100個選出來,然後再遍歷,因為⽆論如何也不會超過100個,最極端的情況就是當前區間只包含⼀個年齡~
7. 1065 單身狗和1090危險物品集裝箱。查詢題目,有兩種方法,第一種就是將要查詢的人排序,然後通過二分查詢(兩邊都要)確定是否含有衝突元素。第二種就是通過兩個map用來找對稱值。其實還有一種大陣列方法,使用對應下標(這個只能在單身狗中使用)
8. 1095 解碼PAT准考證,大量對點查詢使用map或者unordered_map,使用printf就不會超時啦。排序傳參建議⽤引⽤傳參,這樣更快,雖然有時候不⽤引⽤傳參也能通過,但還是儘量⽤,養成好習慣

重要題目:
雜湊雜湊:⽤陣列 hash[26] 或者 hash[10] 儲存某個字⺟或者數位出現的次數/是否曾經出現過;⽤ hash[256] 儲存某個 ASCII碼 字元是否出現過, exist[10000] 、 cnt[10000] 同理。只是使用這些操作時,使用hash雜湊比map快上不少;
1005 還是3n+1:採用的是打表法(如果存取次數多,且範圍小可以打表)[[程式碼#1005 注意條件判斷時確定跳過條件的同時要確定程式還會按著你的猜想繼續執行下去,new的使用。還有就是本來可以使用庫函數進行排序但是不熟悉。]]對每⼀個輸⼊的數位n進⾏驗證,把驗證過的數位對應的arr標記為1,然後對這些輸⼊的數位從⼤到⼩排序,輸出所有arr=0的數位即為關鍵數位。

素數:1007 素數對猜想:打表以及判斷函數[[程式碼#1007 素數對猜想 :注意條件不超過N,應該包括N,判斷素數方法i+=2和sqrt;通過isprime函數判斷i和i-2是否是素數不用陣列儲存]]1094 谷歌的招聘:注意開頭的0也要輸出和isprime小於等於一的判斷

	// 素數表的建⽴  
	// ⾸先都標記為1,外層迴圈i從2到√n,內層迴圈j從2開始到i*j<n 把j的i倍都標記為0  
	vector<int> isprime(50000, 1);  
	for (int i = 2; i * i < 50000; i++)  
		for (int j = 2; j * i < 50000; j++)  
			isprime[j * i] = 0;

two pointer :所謂two pointers問題,就是⽤ p 、 q 兩個指標,使⽤ while 語句處理連結串列問題,連結串列什麼的⽤ vector 儲存就好了 1030完美數列 1035 插入與歸併

貪婪演演算法:
1070 結繩:[[程式碼#1070 floor,ceil,(int)round,格式化輸出]]

列印題目:
1027 列印沙漏:[[程式碼#1027 列印沙漏:一個經典的列印題,分析清楚行數與每行列印個數的關係。]] 列印只能一個接一個,一行接一行列印。 1008元素迴圈右移,1050 螺旋矩陣

1066 影象過濾:[[程式碼#1066 影象過濾:大量輸入輸出使用scanf和printf]]

1069 微博轉發抽獎:[[程式碼#1069 微博轉發抽獎:map中使用find的返回值為迭代器,判斷是否為end可分辨找沒找到,同時map會把儲存在其中的東西初始為空或者0,一般來說stl容器都是這樣,然後就是%操作,要注意和排除負數%為0的情況即符合常理]]

1074宇宙無敵加法器和1079延遲的迴文數:[[程式碼#1074宇宙無敵加法器和1079延遲的迴文數:字串(不)同進位制加法實現,注意保證應有的滿數進一,前導後導零和零的輸出,然後就是stoi如果超過範圍會產生越界錯誤,insert方法補足前0,保證位齊:]]和1113錢串子的加法,注意區分和火星數位

1080 mooc最終成績:[[程式碼#1080 mooc最終成績:map<string,stu>把stu裡面的名字取出來作為鍵值,用來讀入個人資訊,然後遍歷map將其stu存在結構體陣列中進行排序,從而實現值排序]]

1081 檢查密碼:[[程式碼#1081 檢查密碼:注意使用者可能輸入空格作為密碼,雖然不允許,但是要告知不合理,所以輸入要getline,但是多行的getline要在迴圈前加入cin.get(),然後就是i和j不要寫錯了]]

1084 外觀數列:[[程式碼#1084 外觀數列:使用at和[]對字串進行修改時,只能對已有字元的位置修改,而且要使字串已有增加只能+=和其他類似方法]]

1010 一元多項式求導:[[程式碼#1010 一元多項式求導:注意輸入時應使用 scanf因為能夠通過可以按住Ctrl+z,再按enter鍵就可以手動結束,而我用的是cin以最後一位為0做為終點,顯然不行。然後注意題目說的零多項式最後要表示為0 0,其實是在說明當只有一項且指數為0(常數項)時,要列印成0 0,用以區分什麼都沒輸入時的情況。題目說的每句話都是很重要的輸入陣列的方式有兩種,一種是按順序係數和指數直接輸入,還有就是在指數對應的位置輸入係數即a [指數]=係數,注意此題有指數遞減等字眼,且輸出方式也是按照原來的遞減所以兩種都可以問題不大,但是要是沒有說是遞減要你從大到小那第二種容易很多,而且還可以相加。第二個程式碼更精巧]]