本文爲PAT甲級分類彙編系列文章。html
計算類,指以數學運算爲主或爲背景的題。ios
題號 | 標題 | 分數 | 大意 |
1058 | A+B in Hogwarts | 20 | 特殊進制加法 |
1059 | Prime Factors | 25 | 分解素因數 |
1060 | Are They Equal | 25 | 必定精度下兩數是否相等 |
1065 | A+B and C (64bit) | 20 | 大數加法與比較 |
1069 | The Black Hole of Numbers | 20 | 黑洞數 |
1073 | Scientific Notation | 20 | 科學計數法還原 |
1081 | Rational Sum | 20 | 有理數加法 |
1088 | Rational Arithmetic | 20 | 有理數運算 |
1096 | Consecutive Factors | 20 | 最長連續因數 |
1100 | Mars Numbers | 20 | 進制轉換 |
這類題,廣泛分數低,大可能是20分。20分題放到乙級也沒啥問題,好比106九、1081等。然而,即便是20分題,也可能要卡好久。函數
我挑了1059、1065、1088、1096共4道來作。spa
題目要求輸出一個數的素因數分解。這麼簡單的題就很少講了吧,直接上代碼:htm
1 #include <iostream> 2 #include <map> 3 #include <cmath> 4 5 bool isprime(int n) 6 { 7 int end = std::sqrt(n) + 1; 8 for (int i = 2; i != end; ++i) 9 if (n % i == 0) 10 return false; 11 return true; 12 } 13 14 int main(int argc, char const *argv[]) 15 { 16 long n; 17 std::cin >> n; 18 std::cout << n << '='; 19 if (n == 1) 20 { 21 std::cout << '1'; 22 return 0; 23 } 24 std::map<int, int> factors; 25 int prime = 2; 26 while (n > 1) 27 { 28 if (isprime(prime) && n % prime == 0) 29 { 30 ++factors[prime]; 31 n /= prime; 32 } 33 else 34 ++prime; 35 } 36 int count = 0; 37 for (const auto& pair : factors) 38 { 39 if (count++) 40 std::cout << '*'; 41 std::cout << pair.first; 42 if (pair.second > 1) 43 std::cout << '^' << pair.second; 44 } 45 return 0; 46 }
其實用 std::vector 也是能夠的,但我懶。blog
須要注意的是 n==1 的檢查,不過這個很容易發現。ci
要求判斷兩個64位整數相加是否大於另外一個數。注意兩個64位整數相加之後若是還保存在64位整數中是可能溢出的,而且test case裏面必定會有這種狀況。數學
因此要用更大的整數來作計算。然而C++標準庫中並無提供128位整數。這時,偉大的g++出現了,它提供了 __int128_t :
1 #include <iostream> 2 3 int main(int argc, char const *argv[]) 4 { 5 int t; 6 std::cin >> t; 7 std::cout << std::boolalpha; 8 9 for (int i = 1; i <= t; ++i) 10 { 11 __int128_t a, b, c; 12 int64_t ta, tb, tc; 13 std::cin >> ta >> tb >> tc; 14 a = ta; b = tb; c = tc; 15 std::cout << "Case #" << i << ": " << (a + b > c) << std::endl; 16 } 17 18 return 0; 19 }
因而這道題就被秒了。
C++標準庫對整數類型的支持不太好,大整數的類只能本身寫。至因而以二進制存儲仍是以十進制存儲,那要看應用場景了。
在一個case上卡了很久,最後發現題目裏有一句要用 long int ,WCNM。
難度沒什麼的,主要是對分子和分母有0或1的特殊狀況的處理。
優雅的我寫了一份很優雅的實現,又是template又是運算符重載的:
1 #include <iostream> 2 #include <exception> 3 4 template <typename T> 5 class Rational 6 { 7 public: 8 Rational() = default; 9 Rational(T _num, T _den) 10 : numerator(_num), denominator(_den) 11 { 12 reduce(); 13 } 14 Rational(const Rational&) = default; 15 Rational& operator=(const Rational&) = default; 16 17 Rational operator+(const Rational& _rhs) const noexcept 18 { 19 return Rational(numerator * _rhs.denominator + _rhs.numerator * denominator, 20 denominator * _rhs.denominator); 21 } 22 Rational operator-(const Rational& _rhs) const noexcept 23 { 24 return Rational(numerator * _rhs.denominator - _rhs.numerator * denominator, 25 denominator * _rhs.denominator); 26 } 27 Rational operator*(const Rational& _rhs) const noexcept 28 { 29 return Rational(numerator * _rhs.numerator, 30 denominator * _rhs.denominator); 31 } 32 Rational operator/(const Rational& _rhs) const 33 { 34 if (_rhs.numerator == 0) 35 throw std::exception(); 36 return Rational(numerator * _rhs.denominator, 37 denominator * _rhs.numerator); 38 } 39 40 template <typename U> 41 friend std::istream& operator>>(std::istream&, Rational<U>&); 42 template <typename U> 43 friend std::ostream& operator<<(std::ostream&, const Rational<U>&); 44 private: 45 static T gcd(T _lhs, T _rhs) 46 { 47 if (_lhs < 0) 48 _lhs = -_lhs; 49 if (_lhs < _rhs) 50 return gcd(_rhs, _lhs); 51 if (_rhs == 0) 52 return _lhs ? _lhs : 1; 53 return gcd(_rhs, _lhs % _rhs); 54 } 55 56 void reduce() 57 { 58 if (denominator < 0) 59 { 60 numerator = -numerator; 61 denominator = -denominator; 62 } 63 auto factor = gcd(numerator, denominator); 64 numerator /= factor; 65 denominator /= factor; 66 } 67 68 T numerator; 69 T denominator; 70 }; 71 72 template <typename T> 73 std::istream& operator>>(std::istream& _is, Rational<T>& _r) 74 { 75 T n, d; 76 std::cin >> n; 77 std::cin.get(); 78 std::cin >> d; 79 _r = Rational<T>(n, d); 80 return _is; 81 } 82 83 template <typename T> 84 std::ostream& operator<<(std::ostream& _os, const Rational<T>& _r) 85 { 86 auto r = _r; 87 bool neg = false; 88 if (r.numerator < 0) 89 { 90 neg = true; 91 r.numerator = -r.numerator; 92 _os << "(-"; 93 } 94 if (r.denominator == 1) 95 _os << r.numerator; 96 else 97 { 98 if (r.numerator >= r.denominator) 99 { 100 _os << (r.numerator / r.denominator) << ' '; 101 r.numerator %= r.denominator; 102 } 103 _os << r.numerator << '/' << r.denominator; 104 } 105 if (neg) 106 _os << ')'; 107 return _os; 108 } 109 110 int main(int argc, char const *argv[]) 111 { 112 Rational<long long> lhs, rhs; 113 std::cin >> lhs >> rhs; 114 std::cout << lhs << " + " << rhs << " = " << (lhs + rhs) << std::endl; 115 std::cout << lhs << " - " << rhs << " = " << (lhs - rhs) << std::endl; 116 std::cout << lhs << " * " << rhs << " = " << (lhs * rhs) << std::endl; 117 std::cout << lhs << " / " << rhs << " = "; 118 try 119 { 120 std::cout << (lhs / rhs) << std::endl; 121 } 122 catch(const std::exception& e) 123 { 124 std::cout << "Inf" << std::endl; 125 } 126 return 0; 127 }
優雅個屁,考試要來不及的。
順便講講寫自定義類(特指有成員方法的類而非簡單的結構體)和模板的技巧。
構造函數最好給一個默認的,Rule of Three/Five規定的方法最好顯式 =default 寫出來。
參數儘可能用引用,看狀況要不要加 const ,大多都是要加的。相應地,成員方法能用 const 修飾的必定要加上去。
對於模板類的友元函數,模板類中還要再套模板,並且要用不同的類型名稱。
以上規則若是不遵照,極可能會編譯錯誤,並且錯誤信息看起來比較累。
要求輸出給定整數的的分解中能夠出現的最長連續因數。乍一看有一點難,但稍微想想就會發現,這串因數不會太長,由於階乘是很大的。因此能夠一個一個試這串因數中的最小數,連續不少個因數只有沒幾種狀況須要試,到連續兩個的狀況,也是O(sqrt(n))時間複雜度能搞定的。至於單個因數則須要單獨討論一下,由於試到根號n就能夠了,而若是不中止的話會超時。
1 #include <iostream> 2 #include <utility> 3 #include <cmath> 4 5 int main(int argc, char const *argv[]) 6 { 7 int n; 8 std::cin >> n; 9 10 try 11 { 12 for (int count = 11; count > 1; --count) 13 { 14 for (int min = 2; ; ++min) 15 { 16 long product = 1; 17 for (int factor = min; factor != min + count; ++factor) 18 product *= factor; 19 if (product > n) 20 break; 21 if (n % product == 0) 22 throw std::pair<int, int>(count, min); 23 } 24 } 25 int end = std::sqrt(n) + 1; 26 for (int factor = 2; factor != end; ++factor) 27 if (n % factor == 0) 28 throw std::pair<int, int>(1, factor); 29 throw std::pair<int, int>(1, n); 30 } 31 catch(const std::pair<int, int>& pair) 32 { 33 std::cout << pair.first << std::endl; 34 for (int i = pair.second; i != pair.second + pair.first - 1; ++i) 35 std::cout << i << '*'; 36 std::cout << pair.second + pair.first - 1 << std::endl; 37 } 38 39 return 0; 40 }
長度的初始值是11,是由於2*3*...*13是大於2^31的,而2*3*...*12是小於2^31的,所以長度不會超過11。
總之,計算類題目沒有難度,可是要避坑。坑依然是邊界條件,包括最小的數(0、一、2等)與很大的數(int放不下的)。