本文爲PAT甲級分類彙編系列文章。html
集合、散列、數學、算法,這幾類的題目都比較少,放到一塊兒講。ios
題號 | 標題 | 分數 | 大意 | 類型 |
1063 | Set Similarity | 25 | 集合類似度 | 集合 |
1067 | Sort with Swap(0, i) | 25 | 經過與0號元素交換來排序 | 數學 |
1068 | Find More Coins | 30 | 子集和問題 | 算法 |
1070 | Mooncake | 25 | 揹包問題 | 算法 |
1078 | Hashing | 25 | 散列 | 散列 |
1085 | Perfect Sequence | 25 | 符合約束的最大數列長度 | 集合 |
1092 | To Buy or Not to Buy | 20 | 判斷子集 | 集合 |
1093 | Count PAT's | 25 | 數子串 | 數學 |
1063就是 std::set 的使用,1092更水,這兩道不作了。算法
集合-1085:數組
集合,表示方法多種多樣,簡單的有用數組表示的並查集,複雜的有基於紅黑樹的 std::set 。數據結構
但我也不知道當時整理的時候怎麼把這道題分到集合裏來了,但還真挺難說這道題怎麼分類的。題目要求從輸入數據中找出最大的子列(不必定連續)知足最大值不超過最小值的給定倍數。函數
實現方法比較簡單,先排序,而後對於每一個數,找到第一個超過它的倍數的數,迭代器相減一下就是長度。查找要用二分不能用線性,會超時。oop
1 #include <iostream> 2 #include <vector> 3 #include <algorithm> 4 5 int main() 6 { 7 long num, para; 8 std::cin >> num >> para; 9 std::vector<long> data(num); 10 for (auto& i : data) 11 std::cin >> i; 12 std::sort(data.begin(), data.end()); 13 int max = 0; 14 for (int i = 0; i != num; ++i) 15 { 16 if (i == 0 || data[i] != data[i - 1]) 17 { 18 auto left = data.begin() + i; 19 auto right = data.end(); 20 while (left < right) 21 { 22 auto mid = left + (right - left) / 2; 23 if (*mid > data[i] * para) 24 right = mid; 25 else 26 left = mid + 1; 27 } 28 int length = right - data.begin() - i; 29 if (length > max) 30 max = length; 31 if (right == data.end()) 32 break; 33 } 34 } 35 std::cout << max; 36 }
散列-1078:spa
純理論地考察散列知識,用單向平方探測法處理衝突。沒什麼好說的,就是很標準的散列。數據結構那個題集中有一道hard version,那個真的難。.net
1 #include <iostream> 2 #include <vector> 3 #include <cmath> 4 5 bool is_prime(int _num) 6 { 7 if (_num == 1) 8 return false; 9 if (_num > 2 && _num % 2 == 0) 10 return false; 11 int end = std::sqrt(_num); 12 for (int i = 3; i <= end; i += 2) 13 if (_num % i == 0) 14 return false; 15 return true; 16 } 17 18 int main(int argc, char const *argv[]) 19 { 20 int m; 21 std::cin >> m; 22 while (!is_prime(m)) 23 ++m; 24 std::vector<bool> hash(m); 25 int n; 26 std::cin >> n; 27 for (int i = 0; i != n; ++i) 28 { 29 try 30 { 31 int t; 32 std::cin >> t; 33 for (int j = 0; j != m; ++j) 34 { 35 int h = (t + j * j) % m; 36 if (hash[h] == 0) 37 { 38 hash[h] = 1; 39 if (i > 0) 40 std::cout << ' '; 41 std::cout << h; 42 throw 0; 43 } 44 } 45 if (i > 0) 46 std::cout << ' '; 47 std::cout << '-'; 48 } 49 catch (...) 50 { 51 ; 52 } 53 } 54 return 0; 55 }
數學-1067:code
乍一看,徹底沒有思路。正常排序哪有這麼玩的?顯然,這道題不考排序,須要一點數學知識把問題轉化一下。
對於一個序列,若是值與位置的集合相等,那麼必定能夠造成若干個環。這道題中須要考慮的是:一元環,即就在應該在的位置上的元素,以及0處於什麼樣的環中。
本身編幾個數據試一下就會發現,須要交換的次數爲:
當0處於0號位置時,序列長度+多元環個數-一元環個數(包括0);
當0號位置不是0時,序列長度+多元環個數-一元環個數-2。
1 #include <iostream> 2 #include <utility> 3 4 int main(int argc, char const *argv[]) 5 { 6 int n; 7 std::cin >> n; 8 std::pair<int, bool> data[n]; 9 int loop = 0; 10 int single = 0; 11 for (auto& i : data) 12 std::cin >> i.first; 13 for (int i = 0; i != n; ++i) 14 { 15 if (data[i].second) 16 continue; 17 data[i].second = 1; 18 if (data[i].first == i) 19 ++single; 20 else 21 { 22 ++loop; 23 int t = data[i].first; 24 while (!data[t].second) 25 data[t].second = 1, t = data[t].first; 26 } 27 } 28 if (data[0].first == 0) 29 std::cout << n + loop - single; 30 else 31 std::cout << n + loop - single - 2; 32 33 return 0; 34 }
數學-1093:
要求數「PAT」的個數。若是一個個數的話確定超時,畢竟題目中都說了數量很大要取模。
正常的算法也不麻煩。先從左向右遍歷,數‘P’的數量,在遇到'A'時記錄下來;再從右向左遍歷,數'T'的數量,也在遇到'A'時記錄下來;最後遍歷,對每一個'A'計算剛纔記錄的數的乘積並累加。
#include <iostream> #include <string> #include <vector> int main() { std::string str; std::cin >> str; std::vector<int> count_p(str.size()); std::vector<int> count_t(str.size()); long count = 0; for (int i = 0; i != str.size(); ++i) if (str[i] == 'P') ++count; else if (str[i] == 'A') count_p[i] = count; count = 0; for (int i = str.size() - 1; i >= 0; --i) if (str[i] == 'T') ++count; else if (str[i] == 'A') count_t[i] = count; count = 0; for (int i = 0; i != str.size(); ++i) if (str[i] == 'A') count += count_p[i] * count_t[i]; count %= 1000000007; std::cout << count; }
算法-1070:
貪心算法,在數據結構的Dijkstra中提到過一句,沒有系統講過。可是,這道題的算法至關顯然,對每種月餅的單價排序,從高到低選擇便可。
1 #include <iostream> 2 #include <iomanip> 3 #include <vector> 4 #include <algorithm> 5 #include <cmath> 6 7 struct Mooncake 8 { 9 double weight; 10 double price; 11 }; 12 13 int main() 14 { 15 int num; 16 double demand; 17 std::cin >> num >> demand; 18 std::vector<Mooncake> product(num); 19 for (auto& p : product) 20 std::cin >> p.weight; 21 for (auto& p : product) 22 std::cin >> p.price; 23 std::sort(product.begin(), product.end(), [](const Mooncake& lhs, const Mooncake& rhs) { 24 return lhs.price / lhs.weight > rhs.price / rhs.weight; 25 }); 26 double total = 0; 27 double profit = 0; 28 for (const auto& p : product) 29 { 30 double weight = p.weight > demand - total ? demand - total : p.weight; 31 total += weight; 32 profit += weight / p.weight * p.price; 33 if (std::abs(total - demand) < 0.001) 34 break; 35 } 36 std::cout.setf(std::ios::fixed); 37 std::cout << std::setprecision(2) << profit; 38 }
算法-1068:
這道題壓軸,由於我不會。
由於不會,因此瞎寫。先寫了個遞歸實現,對每種硬幣的數量枚舉,其中遞歸調用函數。遇到有符合要求的組合時,就拋出異常,將硬幣組合做爲異常參數。結果兩個樣例居然都過了,然而提交的結果是4個accept、1個wrong、2個timeout。畢竟遞歸了,時間複雜度是指數量級的,不超時纔怪呢。
想不出更好的算法,去查了答案。這道題是01揹包問題,屬於動態規劃算法,我還沒學到,難怪不會作。並且這道題是01揹包問題的特殊版,子集和問題,重量和價值都是數的大小。相似的還有著名的兩數和、三數和、四數和等。
核心算法是抄的,來自這篇博客。
1 #include <iostream> 2 #include <vector> 3 #include <algorithm> 4 #include <functional> 5 6 int main() 7 { 8 int num, target; 9 std::cin >> num >> target; 10 std::vector<int> coin(num); 11 for (auto& i : coin) 12 std::cin >> i; 13 std::sort(coin.begin(), coin.end(), std::greater<int>()); 14 std::vector<std::vector<bool>> select(num); 15 for (auto& v : select) 16 v.resize(target + 1); 17 std::vector<int> total(target + 1); 18 for (int i = 0; i != num; ++i) 19 for (int j = target; j >= coin[i]; --j) 20 if (total[j] <= total[j - coin[i]] + coin[i]) 21 { 22 select[i][j] = true; 23 total[j] = total[j - coin[i]] + coin[i]; 24 } 25 if (total[target] != target) 26 { 27 std::cout << "No Solution"; 28 return 0; 29 } 30 int price = target; 31 int which = num - 1; 32 std::vector<int> found; 33 while (price) 34 { 35 if (select[which][price]) 36 { 37 found.push_back(coin[which]); 38 price -= coin[which]; 39 } 40 --which; 41 } 42 int count = 0; 43 for (int i : found) 44 { 45 if (count++) 46 std::cout << ' '; 47 std::cout << i; 48 } 49 }
最後還要說一句,Eva你已是一個大孩子了,要學會本身解決問題,別老是讓我寫算法。關鍵問題是你還老給我出難題。